Возможно, вы уже использовали шаблон стратегии в связи с Spring Boot, где его очень удобно использовать.
Вы просто определяете интерфейс, например (я использую префикс Я
только в этих примерах для ясности):
public interface IOneStrategy { void executeTheThing(); }
Определите некоторые реализации, подобные этой:
@Service("FIRST") public class OneStrategyFirst implements IOneStrategy { @Override public void executeTheThing() { System.out.println("OneStrategyFirst.executeTheThing"); } }
Теперь вы можете просто реализовать сервис, который будет выполнять соответствующую стратегию на основе заданного имени, которое выглядит примерно так:
@Service public class ExecuteStrategyOne { private Mapstrategies; public ExecuteStrategyOne(Map strategies) { this.strategies = strategies; } public void executeStrategyOne(String name) { if (!strategies.containsKey(name)) { throw new IllegalArgumentException("The strategy " + name + " does not exist."); } strategies.get(name).executeTheThing(); } }
В реальном мире вы делаете несколько реализаций интерфейса стратегии, таких как Сначала одна стратегия
, Одна Стратегия Секунда
и Одна Третья стратегия
. Иногда используется параметр выполнить стратегию Один
, который предоставляется API REST или каким-либо другим кодом, специфичным для домена, который нуждается в различных реализациях.
Удобство здесь заключается в том, что Spring Boot (точнее, Spring Framework) обрабатывает внедрение различных реализаций в стратегии |/Сопоставьте внутри
Выполните стратегию Один
с помощью конструктора. В результате получается карта, где ключом является значение, которое задается @Service ("ПЕРВЫЙ")
и значение карты содержит класс экземпляров каждой реализации интерфейса Одна стратегия
которую можно найти.
Действительно удобно.
В реальной жизни бывает так, что вам нужна другая стратегия, в которой используются те же ключи, что и ПЕРВЫЙ
, второй
и ТРЕТИЙ
в примерах? Давайте определим следующее:
@Service("FIRST") public class TwoStrategyFirst implements ITwoStrategy { @Override public void executeTheThing() { System.out.println("TwoStrategyFirst.executeTheThing"); } }
Если вы попытаетесь запустить это приложение Spring Boot, вы увидите подобное исключение:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'FIRST' for bean class [com.soebes.examples.strategies.functions.two.TwoStrategyFirst] conflicts with existing, non-compatible bean definition of same name and class [com.soebes.examples.strategies.functions.one.OneStrategyFirst] at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:349) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:287) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
Итак, что мы можем сделать, чтобы решить проблему, не теряя при этом большей части удобства, которое предоставляет нам Spring Boot здесь?
Сначала мы должны определить в каждом классе реализации стратегии аннотации, подобные этой:
@Service @Qualifier("FIRST") public class TwoStrategyFirst implements ITwoStrategy { @Override public void executeTheThing() { System.out.println("TwoStrategyFirst.executeTheThing"); } }
Используя ключ в другой аннотации, мы предотвращаем дублирование имен компонентов, противоречащих использованию @Service ("ПЕРВЫЙ")
вместо этого. Использование @квалификатора ("ПЕРВЫЙ")
дает нам критерии для обработки этого другого.
Теперь мы должны изменить Выполнить стратегию Один
класс, как показано ниже:
@Service public class ExecuteStrategyOne { private Mapstrategies; public ExecuteStrategyOne(List strategies) { this.strategies = strategies.stream() .collect( toMap(k -> k.getClass().getDeclaredAnnotation(Qualifier.class).value(), Function.identity())); } ... }
Я хотел бы подчеркнуть использование параметра конструктора Список Одна стратегия> стратегии
вместо ранее использованной Map<Строка, I Одна стратегия> стратегии
что удобно для получения списка всех реализаций из данного интерфейса в этот список при загрузке Spring. Одна стратегия> стратегии вместо ранее использованной
Map<Строка, I Одна стратегия> стратегии
this.strategies = strategies .stream() .collect( Collectors.toMap(k -> k.getClass().getDeclaredAnnotation(Qualifier.class).value(), Function.identity()));
Мы проходим через реализации и извлекаем аннотацию @Квалификатор
и считываем значение()
, которое является ключом, который мы хотим иметь. Мы собираем результат с помощью Collectors.toMap
в карту и присваиваем результат переменной экземпляра частная карта<Строка, IOneStrategy> стратегии;
. В зависимости от ваших потребностей, конечно, можно определить переменную экземпляра как final
и вы можете создать неизменяемую карту, используя соответствующий Collectors.to Неизменяемая карта
вместо для отображения(..)
если понадобится.
Таким образом, с помощью нескольких изменений в коде мы можем легко решить проблему наличия разных стратегий, использующих одни и те же ключи в нашем коде.
Данный код доступен в качестве полного рабочего примера на GitHub .
Оригинал: “https://dev.to/khmarbaise/spring-boot-strategy-design-pattern-convenience-and-limitation-2mee”