The Chain of Responsibility Pattern is a design pattern whose intent is to avoid coupling the sender of a request to its receivers by giving more than one object a chance to handle a request. The Chain of Responsibility works like this:
- The "receiver" objects are added to a chain.
- The "sender" object has a reference to the first receiver object in the chain.
- Objects in the chain are linked, typically as a one-directional linked list, with a "next" reference from one node to the next node.
- At some point in the program's life cycle, the sender sends a request to the chain.
- The request is passed along the chain, from one object to the next, until an object (a "handler" or "receiver") handles it. When the request is handled, it is not passed on any longer.
- If no objects handle the request, a default object at the end of the chain can be made to handle it, or it may be that no objects handle the request.
- In a variation of the pattern, multiple objects may handle the request.
Why use the Chain of Responsibility pattern
You'll use the Chain of Responsibility design pattern in your Java projects when:
- You want to decouple the sender and receivers of a request.
- You want to allow handlers to be added dynamically in your code.
- The sender of the request needs to think of this as a "launch and leave" operation, where they place their request at the entrance of the chain pipeline, and then forget about it.
- When multiple objects can react to a request, and typically the order in which the request is handled is important. The most specific objects are at the beginning of the chain, and the most general handlers are at the end of the chain.
Benefits of the Chain of Responsibility pattern
The Chain of Responsibility pattern has many benefits to your Java code, including:
- The Chain of Responsibility pattern promotes the idea of "loose coupling", which is always a good thing.
- The chain of handlers can be easily and dynamically changed without affecting the sender of the requests.
- The sender of the request has no idea how many handlers there are.
- The sender of the request can "fire and forget" the request.
How to implement the Chain of Responsibility pattern
Here's a quick look at how to implement the Chain of Responsibility pattern in your Java classes:
- You'll typically create a base "Chain" or "Node" class that maintains a reference to the next link in the chain.
- Each Chain subclass has its own logic to handle the request from the sending object.
- Chain objects are added to the chain, with the most specific handlers added first in the chain, and more general handlers added later. You may even add a "default" handler at the very end of the chain.
- The logic in each chain/handler class is such that if the object handles the request, the request is not passed any further down the chain, but if the request is not handled, the request is passed to the next item in the chain.
- When needed, the "sender" object sends a request to the first node in the chain. The request is either handler, or passed to the next node, and so on.
Chain of Responsibility pattern - Examples
The Chain of Responsibility pattern is one of the few design patterns I haven't used, so I don't have any examples I can pull directly from some Java code to share with you.
Here are three Java Chain of Responsibility pattern examples I'm familiar with:
- Java servlet filters implement the Chain of Responsibility pattern, though they don't follow the pattern exactly. Specifically, the request continues to move down the chain from one node to the next as the request is "filtered."
- The original Java AWT Event Model also followed the Chain of Responsibility pattern. However, in that case, this pattern created several problems (including the need to subclass AWT components, and event-handling performance), and the Java architects took the drastic step of rewriting the Java GUI Event Model, and replacing it with more of an Observer design pattern.
- While I think their example can be improved, the Wikipedia website has a short Java example that demonstrates a chain.
Beyond those Java Chain of Responsibility examples, here are other examples I've seen mentioned in design patterns books and on the internet:
- The c2.com website states that a mechanical coin sorting bank is a physical implementation of the Chain of Responsibility pattern. As they state, "Rather than having a separate slot for each coin denomination coupled with a receptacle for the denomination, a single slot is used."
$PATHenvironment variable on Unix and Linux computers can be thought of as a chain, where the first matching directory responds to the "request."
- The concept of Unix/Linux pipes (pipelines) can arguably be thought of as a Chain of Responsibility, though really they are much more of a filtering mechanism.
- Conceptually, class inheritance and even method overloading can be thought of as a Chain of Responsibility. For instance, if you have a base class that has a method, and that method is either overridden in sub-classes, or not overridden, from a conceptual standpoint you can imagine that the JVM must go from one class to another to see which class will respond to the method call. (This is more of a conceptual exercise, as modern Java compilers don't work this way.)
- ATMs are another physical device that act as a Chain of Responsibility device when they determine which bills/coins they should give to you when you request money.
Related Design Patterns
The Chain of Responsibility pattern is related to other design patterns, including these:
- The Chain of Responsibility can use the Command Pattern to represent requests as objects.
- Several design patterns address how to decouple senders of requests and receivers with different trade-offs, including Command, Mediator, and Observer
- Chain of Responsibility is often used in combination with Composite. From the Smalltalk Companion, "When a Composite delegates a message to its children or a Decorator delegates to its component, this is Chain of Responsibility."
Consequences of the Chain of Responsibility pattern
The Chain of Responsibility pattern has the following consequences:
- Multiple handlers may be able to handle a request.
- The sender of the request only has a reference to the first handler.
- The sender doesn't know how many handlers are in the chain.
- The sender has no control over the handlers.
- Only one handler in the chain should handle the request.
- Some requests may not be handled at all.
- Changing the list of handlers doesn't affect the sender.
As mentioned earlier, you are free to bend or break these rules as you see fit, but this article attempts to describe the "pure" Chain of Responsibility design pattern.
I hope this discussion of the Chain of Responsibility pattern in Java has been helpful. As usual, if you have any comments or questions, please leave a note in the Comments section below.