Your Guide to Design Patterns – Chain of Responsibility

Design Patterns - Chain of Responsibility Pattern

This week we’ll continue our exploration of design patterns and look at a very commonly used behavioural pattern, the Chain of Responsibility pattern.

The Chain of Responsibility Pattern

The Gang of Four book defines the Chain of Responsibility pattern as follows:

“Avoid coupling the sender of a request to its receiver by giving more than one object the chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.”

This pattern decouples classes by passing a request from one class to another until the request is recognised. The receiving objects are chained, and the request is passed along the chain until a specific object handles it. Each object in the chain is given a chance to handle the request.

The Chain of Responsibility pattern allows us to define separate handler objects that all conform to a particular interface. This ensures that handlers are independent from each other and loosely coupled.

Chain of Responsibility in the Java APIs

Some examples from the standard Java APIs:

  • The Java 1.0 hierarchical event model is an example of Chain of Responsibility. It is now obsolete and used only for backward compatibility.
  • Filters in the Servlet API: javax.servlet.Filterjavax.servlet.FilterChainjavax.servlet.http.HttpFilter.
  • Interceptors in EJB: javax.interceptor.Interceptor.
  • AOP aspects in Spring and other AOP implementations.
  • Logical and SOAP handlers with javax.jws.HandlerChain.
  • Logging in java.util.logging.Logger and java.util.logging.Handler.

Chain of Responsibility: Example Problem

In our first person shooter game, we’ve already sent out a desperate call to headquarters (HQ) for reinforcements. There could be any number of calls coming in with other requests: food and supplies, medical assistance, aerial support to bomb a target, even evacuations. This game is getting a bit too real!

If we had one method handling all of these different requests, we would have a series of if...else statements that would not be particularly object-oriented, and to top it, would be tedious to modify, maintain and test. Some example code follows:

public class HeadQuarters {

    public void processRequest (String request) {

        // check the incoming request string for null, 
        // then call trim() and toUpperCase() on it.

        if (request.contains("FOOD")) {
            // respond with food delivery 
        }
        else if (request.contains("MEDIC")) {
            // respond with medicine and doctors 
        }
        else if (request.contains("AERIAL")) {
            // respond with aerial support bombers 
        }
        else if (request.contains("EVAC")) {
            // respond with evacuation helicopter 
        }
        else
            // stand by for the first available operator... 
    }

    // other code not shown 

} // end of class 

There must be a better way than this, surely!

Chain of Responsibility: Example Solution

Chain of Responsibility to the rescue! Let’s create a series of handlers, each of which will respond to a specific request. We will chain these handlers together, passing the request from one handler to another until the request is dealt with. If a specific handler can’t handle the request, it passes it on to the next handler in the chain.

First of all, we create an interface for the handlers:

public interface RequestHandler {
    public void processRequest(String request);
    public void setNextHandler(RequestHandler handler);
} // end of class 

We’ll also create an AbstractRequestHandler class to provide appropriate default behaviour for the RequestHandler interface:

public abstract class AbstractRequestHandler implements RequestHandler {
    private RequestHandler nextHandler;

    @Override
    public void setNextHandler(RequestHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public void processRequest(String request) {
        boolean match = false;
        // Handle the request here if the word list is empty 
        if (wordList().length == 0) {
            match = true;
        } else {
            // Search for any of the keywords
            for (String word : wordList()) {
                if (request.indexOf(word) >= 0) {
                    match = true;
                    break;
                }
            }
        } 
        // Can we handle the request here? 
        if (match) {
            handleRequest(request);
        } else {
            // No match so pass the request along the chain 
            nextHandler.processRequest(request);
        }
    }

    // to be implemented by the concrete handler subclasses 
    protected abstract String[] wordList();
    protected abstract void handleRequest(String request);

} // end of class 

The setNextHandler() method is passed a RequestHandler which is the next handler to call if the current handler can’t respond to the request.

The processRequest() method takes the request as its argument. It compares the incoming request with the list of keywords set up in the wordList() method. If any of these words are contained in the request string, then the active handler can respond to the request. If there isn’t a word match, then the active handler passes the request to the next handler in the chain.

The wordList() and handleRequest() methods are appropriately implemented by the concrete handler subclasses as follows:

public class FoodRequestHandler extends AbstractRequestHandler {
    protected String[] wordList() {
        return new String[]{"food", "water", "rations", "hungry"};
    }
    protected void handleRequest(String request) {
        System.out.println("Request handled by kitchen.");
    }
}

public class MedicRequestHandler extends AbstractRequestHandler {
    protected String[] wordList() {
        return new String[]{"medic", "medicine", "doctor"};
    } 
    protected void handleRequest(String request) {
        System.out.println("Request handled by medical department.");
    }
}

public class EvacuationRequestHandler extends AbstractRequestHandler {
    protected String[] wordList() {
        return new String[]{"evac", "evacuation"};
    }
    protected void handleRequest(String request) {
        System.out.println("Request handled by evacuations.");
    }
}

public class AerialSupportRequestHandler extends AbstractRequestHandler {
    protected String[] wordList() {
        return new String[]{"bombing", "aerial"};
    }
    protected void handleRequest(String request) {
        System.out.println("Request handled by aerial support.");
    }
}

public class OperatorRequestHandler extends AbstractRequestHandler {
    protected String[] wordList() {
        return new String[0]; // match anything 
    }
    protected void handleRequest(String request) {
        System.out.println("Request handled by operator.");
    }
}

A client class using the handlers could be written as follows. To help set up the request handler chain, we could add a static method to the client class:

public class ChainOfResponsibilityClient {

    public static RequestHandler getHandlerChain(){

        // Create the handlers 
        RequestHandler food     = new FoodRequestHandler();
        RequestHandler medic    = new MedicRequestHandler();
        RequestHandler evac     = new EvacuationRequestHandler();
        RequestHandler aerial   = new AerialSupportRequestHandler();
        RequestHandler operator = new OperatorRequestHandler();

        // Chain them together 
        food  .setNextHandler(medic);
        medic .setNextHandler(evac);
        evac  .setNextHandler(aerial);
        aerial.setNextHandler(operator);

        return food; // Start of the handler chain 
    }

    public static void main (String args[]) {

        RequestHandler chain = getHandlerChain();

        String request = "We need aerial support!";
        System.out.println(request);
        chain.processRequest(request);

        request = "We're hungry!";
        System.out.println(request);
        chain.processRequest(request);

        request = "We're lost and confused!";
        System.out.println(request);
        chain.processRequest(request);
    }
} // end of class 

Obviously in a production environment it would be more flexible to set up the chain using an external configuration mechanism (such as XML) rather than in code which would need to be recompiled each time we added or removed a handler from the chain.

Comparison with Other Design Patterns

Other patterns such as Command, Mediator and Observer also decouple the objects making the requests from the objects receiving them, but they all implement it in different ways. Chain of Responsibility uses a chain of handlers to process the request.

Patterns can be combined to create flexible code. For example, the Command pattern can be used to wrap the request, and Chain of Responsibility can decouple the requests from their handlers.

As always, please share your comments and questions.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.