Chain of Responsibility
Chain of responsibility is a design pattern that allows to separate the sender of a request from its one or more recipients. The request is passed from recipient to recipient till it gets to the one competent to deal with it. The implementation should also consider the situation when the recipient is not found.
Motivation
If we tightly connected the class that sends a request and the class that processes it, we'd violate Low Coupling. We'd also lose the possibility to assign multiple handlers processing the request. This is often used in practice when applying various filters that are in a chain, one after another, and process the request somehow, gradually.
The use isn't limited only to linear data structures, but passing the responsibility can also be done at the element-to-parent level in tree structures. Sometimes, the pattern is combined with the Composite pattern, that defines the most efficient way to create such tree structures. This tree can sometimes be referred to as the Tree of Responsibility. Some frameworks use Chain of Responsibility to implement their event models.
Pattern
It's probably not surprising that Handler
, the recipient of the
request, is defined here as an interface. The sender of the request communicates
with this interface and it's implemented by the individual request handlers of
the chain or tree. These are connected to each other by references.
The handler typically provides methods for setting the next handler. Moreover, the method to handle the request, this method is polymorphic. And finally, the method to pass the request to the next handler. This can happen even if the request was handled only partially or not at all, e.g. because the handler is busy or not competent to handle the request.
Example
There are certainly many examples of Chain of Responsibility, as this is how many services work. For example, when ordering goods from abroad, your request is handled by a chain of post companies. Real practical use, however, is usually the implementation of some filters. These are chained one after another and block the request if it's not valid. Only the last link of such a chain would actually handle the request.
Let's create an example of a chain of such filters. We're gonna filter email requests which will first go through a spam filter, then by a user filter, and then potentially get to the handler that puts them in the inbox folder. Another handler could display a new email notification. Let's make an example of receiving an email that goes through several filters. We'll be able to add and change the filters freely thanks to the pattern.
Let's define the abstraction for the chain handlers:
public abstract class RequestHandler { private RequestHandler next; public RequestHandler setNext(RequestHandler next) { this.next = next; return next; } protected void passToNextHandler(Request request) { if (next != null) next.handleRequest(request); } public abstract void handleRequest(); }
Note that the setNext()
method returns the next handler in the
chain. This is because we can use Method Chaining to put the whole chain
together. E.g. this way:
spamFilter.setNext(userFilter).setNext(incommingMailHandler);
We can simply construct the whole chain on a single line. The concrete handlers can look as follows:
public class SpamFilter extends RequestHandler { public void handleRequest(Request request) { if (!request.Email.Text.Contains("free pills")) { // Simple spam filter passToNextHandler(request); } } } public class UserFilter extends RequestHandler { public List<String> restrictedAddresses = new ArrayList<String>(); public void handleRequest(Request request) { if (!restrictedAddresses.contains(request.email.address)) { // Simple user filter passToNextHandler(request); } } } public class IncommingMailHandler extends RequestHandler { private IncommingMail mail; public IncommingMailHandler(IncommingMail mail) { this.mail = mail; } public void handleRequest(Request request) { mail.add(request.email); } }
The features of the individual filters are, of course, extremely simplified
and illustrative only. We'd make the whole chain working by passing a request to
its first link, SpamFilter
.
Related Patterns
- Composite - Chain of responsibility isn't restricted to linear structures and can be also applied to trees, their implementation is the concern of the Composite pattern