Выберите Spring bean в соответствии с параметром enum

В моем приложении Spring Boot у меня есть понятие Stage и StageProcessor, которое обрабатывает Stage. Stage имеет свойство StageType enum. У меня есть разные реализации интерфейса StageProcessor, и эти реализации являются компонентами Spring. Теперь у меня есть еще один компонент Spring, WorkflowProcessor, который должен вызывать соответствующий StageProcessor в зависимости от StageType из Stage. До сих пор я пришел к следующему:

@Service
public class StageConfig {
    @Autowired
    private StageProcessorA stageProcessorA;
    @Autowired
    private StageProcessorB stageProcessorB;

    public StageProcessor getProcessor(Stage stage) {
        switch(stage.getType()) {
            case A:
                return stageProcessorA;
                break;
            case B:
                return stageProcessorB;
                break;
        }
    }
}

Интересно, не хватает ли мне какого-либо шаблона проектирования или механизма Spring. Есть идеи по лучшему дизайну?

Хорошо, что я бы сделал, это использовать ApplicationContextAware, а затем вернуть правильный объект методом getBean.

Angelo Immediata 05.03.2019 17:48

Является ли Stage интерфейсом? Это фасоль? Есть только один? Что определяет, будет ли это А или В?

Michael 05.03.2019 17:57

Stage не является ни интерфейсом, ни bean-компонентом. Это сущность. StageType определяет, какой процессор использовать.

Andrey Tararaksin 05.03.2019 18:09

Ты не понял. Что определяет, будет ли stage.getType() A или B?

Michael 05.03.2019 18:29

Это просто бизнес-данные, ранее сохраненные в БД.

Andrey Tararaksin 06.03.2019 09:16
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
5
367
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это немного зависит от специфики вашего случая. Во многих случаях этапы на самом деле не жестко закодированы, как в перечислении, а настраиваются для различных систем. Кроме того, чем больше потенциальных этапов у вас есть, тем больше окупается более подробная настройка, но избегающая повторений.

В общем, я бы порекомендовал здесь шаблон распознавателя Spring. Код выглядит примерно так, где KeyType обычно это перечисление или строка. Общая идея заключается в том, что каждая реализация сообщает вам, какие вещи (этапы, типы, параметры и т. д.) она может обрабатывать, а затем вы ищете соответствие. (Вариант, в котором нет прямого поиска сопоставления, состоит в том, чтобы иметь boolean canHandle(something) и повторять, пока не найдете его.)

interface StageProcessor {
    OutputType process(Stage stage);
    KeyType stageKey();
}

@Service
class StageProcessors {
    Map<KeyType, StageProcessor> stageProcessors;

    StageProcessors(Collection<StageProcessor> processors) {
        stageProcessors = processors.stream().collect(groupingBy(StageProcessor::stageKey));
        assert stageProcessors.size() == expectedNumberOfProcessors;
    }

    StageProcessor getProcessor(KeyType stage) {
        // although usually your service would take care of dispatching directly
        return stageProcessors.get(stage);
    }
}

(И в качестве примечания: Избегайте инъекций поля.)

Это определенно более чистый дизайн. Я буду использовать его, дополненный идеями @GabiM. Что касается внедрения в поле, это просто для более чистого кода прототипа, я буду придерживаться внедрения конструктора в реальном коде. Спасибо!

Andrey Tararaksin 06.03.2019 09:32
Ответ принят как подходящий

Это полностью основано на идее @chrylis, просто процессорам не нужно менять свой API для этого, вы можете просто сделать это с помощью аннотации. Также бывает случай, когда у вас несколько процессоров для одного типа.

interface StageProcessor {
    OutputType process(Stage stage);
}

@Component
@Processes(StageType.A)
class StageProcessorA implements StageProcessor{
      OutputType process(Stage stage){
        //validate stage is StageType.A
      }
}

@interface Processes{
    StageType type;
    StageType getType(){
        return type;
    }
}

@Component
@Processes(StageType.B)
class StageProcessorB implements StageProcessor{

}

@Service
class StageProcessors {
    Map<StageType, List<StageProcessor>> stageProcessors;

    StageProcessors(Collection<StageProcessor> processors) {
        Map<StageType, List<StageProcessor>> map = new HashMap<>();
        for (StageProcessor processor : processors) {
            StageType stageType = processor.getClass().getAnnotation(Processes.class).getType();
            map.computeIfAbsent(stageType, k -> new ArrayList<>()).add(processor);
        }
        stageProcessors = map;
        assert stageProcessors.size() == expectedNumberOfProcessors;
    }

    List<StageProcessor> getProcessors(StageType stage) {
        return stageProcessors.get(stage);
    }
}

Другие вопросы по теме