Chain of responsibility pattern
The chain of responsibility (CoR) sends a request to a chain of objects. Thus, a request can be handled by one or more objects.
problems
Suppose you have a request that can be solved in different ways. For example, authorization can be done simply via username/password or with external accounts like Facebook via OAuth.
Second case if you have multiple handlers. For example, some data has been changed. Then the first handler will save data to the local database. The second handler will write logs. The third will notify the GUI, etc. We can treat one handler as main handler and others as pre or post processors.
advantages
- Decouples the request senders from the request handlers.
- Allows to change chain dynamically.
- Handler can decide to pass or not to pass the request further.
chain of responsibility vs array/list
// using array
for(int i = 0; i < handlers.length; ++i) {
handlers[i].handle(request);
if(handlers[i].handled()){
break;
}
}
// using CoR
getChain().handle(request);
Why not use list or array of handlers? Answer is the first advantage of CoR - decoupling request senders and handlers. This mean:
- requester knows only a reference to one handler
- requester doesn't know about of the number of possible handlers
- requester doesn't know which handler will handle
- requester doesn't have any control over the handlers
example
public interface RequestHandler {
void setNextRequestHandler(RequestHandler requestHandler);
boolean process(RequestData requestData);
}
public class DBDataHandler implements RequestHandler {
private RequestHandler nextRequestHandler;
@Override
public void setNextRequestHandler(RequestHandler requestHandler) {
nextRequestHandler = requestHandler;
}
@Override
public boolean process(RequestData requestData) {
// ... doing stuff
return nextRequestHandler == null ? true :
nextRequestHandler.process(requestData);
}
}
// ...
public class AnyHandler implements RequestHandler {
private RequestHandler nextRequestHandler;
@Override
public void setNextRequestHandler(RequestHandler requestHandler) {
nextRequestHandler = requestHandler;
}
@Override
public boolean process(RequestData requestData) {
// ... doing stuff
return nextRequestHandler == null ? true :
nextRequestHandler.process(requestData);
}
}
public RequestHandler getHandlersConfig1 {
RequestHandler requestHandlers [] = new RequestHandler[] {
new DBDataHandler(),
new RDBDataHandler(),
// ...
new AnyHandler()
};
int cnt = requestHandlers.length-1;
for(int i=0; i < cnt; ++i){
requestHandlers[i].setNextRequestHandler(requestHandlers[i+1]);
}
return requestHandlers[0];
}
// in classic CoR, only one handler actually handles the request
@Override
public boolean process(RequestData requestData) {
// ... try doing stuff
if(!isHandled){
return nextRequestHandler != null? false :
nextRequestHandler.process(requestData);
}
return true;
}