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

  1. Decouples the request senders from the request handlers.
  2. Allows to change chain dynamically.
  3. 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; }