In this article I’d like to present the usage
of this pattern in an IOC Container, where the Handlers aren’t added to the HandlerSet
list but provided from the container. In this way you can add new responsibility
to the system simply adding a new Handler in the container without changing other
parts of implemented code (es. the HandlerSet), in full compliance with
the Open closed principle.
For coding I’ll use the Spring Framework (JAVA) because
it has a good IOC Container and provides a set of classes to manage with that. Inversion of control principle and the Dependency Injection are first class citizens in the SpringFramework.
Here is the UML class diagram with 3 responsabilities X,Y,Z and a brief description of the solution adopted.
Here is the UML class diagram with 3 responsabilities X,Y,Z and a brief description of the solution adopted.
@Component
public class XHandler implements Handler {
@Override
public Result handle(Request request) {
return ((RequestX) request).doSomething();
}
@Override
public boolean canHandle(Request request) {
return request instanceof RequestX;
}
}
@Component annotation on XHandler tells to Spring to instantiate an object of this type in the IOC container.
public interface HandlerManager { Result handle(Request request) throws NoHandlerException; }
@Service public class CtxHandlerManager implements HandlerManager { private ApplicationContext applicationContext; @Value("${base.package}") private String basePakage; @Autowired public CtxHandlerManager(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public Result handle(Request request) throws NoHandlerException { OptionalhandlerOpt = findHandler(request); if ( !handlerOpt.isPresent() ) { throw new NoHandlerException(); } Handler handler = handlerOpt.get(); return handler.handle(request); } private Optional<Handler> findHandler(Request request) { ClassPathScanningCandidateComponentProvider provider = createComponentScanner(); for (BeanDefinition beanDef : provider.findCandidateComponents(basePakage)) { try { Class clazz = Class.forName(beanDef.getBeanClassName()); Handler handler = (Handler) this.applicationContext.getBean(clazz); //find responsible handler for the request if (handler.canHandle(request)) { return Optional.ofNullable(handler); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } return Optional.empty(); } private ClassPathScanningCandidateComponentProvider createComponentScanner() { ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); provider.addIncludeFilter(new AssignableTypeFilter(Handler.class)); return provider; } }
CtxHandlerManager works like an Handler dispatcher. The handle method finds the Handler and calls its handle method which invokes the doSomething method of the Request.
In the findHandler method I use the Spring ClassPathScanningCandidateComponentProvider object with an AssignableTypeFiler to the Handler class. I call the findCandidateComponent on a basePackage (the value is set by @Value Spring annotation) and for each candidate the canHandle method check the responsability. And that's all!
In Sender class the HandlerProvider implementation (CtxHandlerManager)is injected by Spring IOC by "Autowiring":
In the findHandler method I use the Spring ClassPathScanningCandidateComponentProvider object with an AssignableTypeFiler to the Handler class. I call the findCandidateComponent on a basePackage (the value is set by @Value Spring annotation) and for each candidate the canHandle method check the responsability. And that's all!
@Service public class Sender { @Autowired private HandlerManager handlerProvider; public void callX() throws NoHandlerException { Request requestX = new RequestX(); Result result = handlerProvider.handle(requestX); ... }
This solution let you to add new responsibility simply creating new Request implementation and a new Handler implementation to manage it. By applying @Component annotation on the Handler you allow Spring to autodetect the class for dependency injection when annotation-based configuration and classpath scanning is used. On application reboot, this class can be provided by the IOC Container and instantiated simply invoking the HandlerManager.
In the next post I’d like present a possible implementation of a Component Factory using the "Set of responsability" pattern in conjunction with another interesting pattern, the "Builder pattern".
Happy coding
Mauro
No comments:
Post a Comment