Рубрики
Без рубрики

Пружинный ботинок: Шаблон дизайна стратегии – Удобство и ограничения

Возможно, вы уже использовали шаблон стратегии в отношениях с Spring Boot, где он очень удобен… С тегом java, весенняя загрузка.

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