Возможно, вы уже использовали шаблон стратегии в связи с 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 Map strategies;
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 Map strategies;
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”