Search code examples
javaspringspring-bootdesign-patterns

Select Spring bean according to enum param


In my Spring Boot app I have a notion of Stage and StageProcessor which processes the Stage. Stage has a StageType enum property. I have different implementations of StageProcessor interface, and these implementations are Spring beans. Now, I have another Spring bean, WorkflowProcessor, which needs to invoke appropriate StageProcessor depending on StageType of the Stage. So far I have come up with the following:

@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;
        }
    }
}

I wonder if I am missing any design pattern, or Spring mechanism. Any ideas of a better design?


Solution

  • This is totally based on @chrylis idea just there's no need for the processors to change their API for this, you can just do it with an annotation. Also, there's the case when you have multiple processors for one type.

    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);
        }
    }