ip696
ip696

Reputation: 7094

Best way for imlement Strategy design pattern in Spring

I want implement strategy design pattern in spring boot application. I create BeanPostProcessor for construct strategy resolver:

@Component
public class HandlerInAnnotationBeanPostProcessor implements BeanPostProcessor {

    private final UnpHandlersResolver unpHandlersResolver;

    public HandlerInAnnotationBeanPostProcessor(UnpHandlersResolver unpHandlersResolver) {
        this.unpHandlersResolver = unpHandlersResolver;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Annotation[] annotations = bean.getClass().getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof HandlerIn) {

                if (bean.getClass() != UnpHandler.class)
                    throw new RuntimeException("Not UnpHandler bean annotated by HandlerIn");

                SmevMessageType[] type = ((HandlerIn) annotation).type();
                for (SmevMessageType smevMessageType : type) {
                    unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
                }
            }
        }
        return bean;
    }
}

And I create resolver:

@Slf4j
@Component
public class UnpHandlersResolverImpl implements UnpHandlersResolver {

    private Map<SmevMessageType, UnpHandler> map = new HashMap<>();

    @Override
    public void setHandler(SmevMessageType messageType, UnpHandler unpHandler) {
        map.put(messageType, unpHandler);
    }

    @Override
    public UnpHandler getUnpHandler(SmevMessageType type) {
        UnpHandler sendRequestHandler = map.get(type);
        if (sendRequestHandler == null)
            throw new IllegalArgumentException("Invalid SendRequestHandler type: " + type);
        return sendRequestHandler;
    }
}

My BeanPostProcessor scan all beans with annotation HandlerIn and add to resolver's mup. I think it's wrong to do that:

unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);

But I not understand how can I add find beans to resolver. Before this implementation I faind beans in @Postconstruct method of resolver like:

context.getBeansWithAnnotation(HandlerIn.class);

But in this solution I have context in resolver and I think is bad.

Tell me how to properly implement what I want? In short, I want to have a set of classes that implement different behaviors. And the class that controls them. Give the class a parameter so that he chooses the right strategy and gives it to me. Like this:

Handler handler = handlersResolver.getHandler(messageType);
Result result = handler.somthing(param);

Upvotes: 4

Views: 4129

Answers (1)

crabe
crabe

Reputation: 332

I'm going to try to make a simple example.

Interface Greeting {

void sayHello();

String getSupportedLanguage();

}

Then you have X number of implementations and you can loop through them in your "resolver"'s constructor to build the map. (I've seen this called a Proxy or a Decorator in code though, i.e. GreetingProxy or GreetingDecorator)

@Service
public GreetingResolver {

  private Map<String, Greeting> languageToGreetingMap = new HashMap<>();

  @Autowired
  public GreetingResolver(List<Greeting> greetings) {
    for (Greeting greeting : greetings) {
      languageToGreetingMap.put(greeting.getSupportedLanguage(), greeting);
    }
  }

  public void sayGreetingForLanguage(String language) {
    languageToGreetingMap.get(language).sayHello();
  }

}

This is a basic example of how one can do the strategy pattern in Spring. Every interface implementation of "Greeting" only knows about itself and what it can support. We then autowire all implementations in a list and loop through to create the map once and then during runtime only the relevant entry from the map in retrieved and used.

Note: this was typed "free hand" directly in the web page so please forgive any typos in the code.

Upvotes: 1

Related Questions