Chain of Responsibility
Chain of Responsibility is a behavioral design pattern that lets you avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
Imagine that you are working on an online ordering system. Your first task was to restrict access to the system so that only authenticated users could be able to create orders. In addition, some users with administrative permissions had to have full access to all orders.
You quickly realized that these checks must be performed sequentially. The program was able to try to authenticate a user at any time, as long as user's credentials were passed along the request. But if failed to authenticate the user, it would be impossible to check user's permissions.
Over the course of several months, you had implemented a few more of these sequential checks.
One of your colleagues suggested that it is unsafe to pass raw data straight to the processing code. So you added an extra validation step to sanitize request's data.
Later someone else noticed that system is vulnerable to brute force password cracking. To negate that, you promptly added another check to filter repeating failed requests with the same username.
At some point, you even added caching to the ordering form to increase its performance under heavy load.
Unfortunately, with every new feature, the code was becoming more and more bloated. Moreover, you had to duplicate code in several places to protect other pages that required just a portion of these checks.
The system became too hard and expensive to maintain. But one day you received a task to refactor the whole thing...
Like many other behavioral design patterns, the Chain of Responsibility relies on transforming behaviors into stand-alone objects. In our case, each check will be moved to a separate class with a single method that runs the check. The method will be able to accept request data through its parameters.
Now, the interesting part. The pattern suggests to link these handler objects into a chain. Each handler will get a field for storing a reference to the next handler in the chain. Whenever a handler receives a request, it may pass it to the following handler on the chain. The request would travel along the chain until all handlers had a chance to process it.
And the last thing. A handler does not have to pass a request further. There are two popular ways to exploit this idea.
In our example with access filtering, handlers would line-up and execute their checks one-by-one. The flow terminates only when one of the checks fails or when the end of the chain is reached.
But there is a slightly different approach, where handlers pass a request along only if they are not capable of processing it at all. Otherwise, they do their thing and terminate further execution. This option is very common when dealing with events in graphical user interface components.
For instance, when a user clicks a button, the event is propagated through a chain of components that starts with the button, goes along its parents, like forms and panels, and ends up with the main application window. The event is processed by the first component in that chain capable of handling it. This example is also noteworthy because it shows that a chain can always be extracted from a tree structure.
It is crucial that all handler classes follow the same interface. It will let you compose a chain at runtime using various handlers without coupling code to their concrete classes. Each concrete handler should only care about the following one having the
You have just bought a new video card for your PC. Windows has detected and enabled it automatically. But your beloved Linux, however, refused to work with the new hardware. With a small glimpse of hope, you call the tech support.
The first thing you hear is a robot's voice of the autoresponder. It suggests nine popular solutions to various problems, none of which is related to yours. After a while, robot connects you to the live operator.
Alas, the operator can not suggest anything specific. It only shoots you with long excerpts from FAQ section. After hearing "did you try turning it off and on again" for the 10th time, you ask to connect you to a proper engineer.
The operator quickly passes your call to the engineer that inhabits a lonely server room in a basement of an office building. Finally, the engineer tells you where to download proper drivers for the card and how to install them in Linux. You end the call, bursting with joy.
Handler declares the common interface for all concrete handlers. Usually, it may contain just a single method for handling requests, but sometimes it may also have another method for setting the next handler on the chain.
Base Hander is an optional class that contains boilerplate code responsible for building and maintaining a chain of objects.
The class may contain a field for storing a reference to a next object in the chain. Using this field, clients can link several handlers into one chain. The field can be controlled via the constructor or a designated setter. The class may also have a basic handling method implementation, which would pass execution to the next handler after checking for its existence.
Concrete Handlers contain the actual code for processing requests. Upon receiving a request, a handler must decide whether or not to process it, and additionally, whether or not to pass it along the chain.
Handlers are usually self-contained and immutable, accepting all necessary data just once, via constructor parameters.
Client may compose chains just once or do it dynamically, depending on the program's logic. Note that a request could be sent to any handler in the chain, it does not always have to be the first one.
In this example, the Chain of Responsibility is responsible for showing a contextual help related to the active UI element.
The GUI elements are structured into a tree. The
Dialog class that renders the main window is the root of the tree. The middle layer composed with
Panels. The leaf-level
TextEdits and others.
Component is capable of showing contextual tooltips, as long as it has some help text assigned. Some complex components have their own way of showing contextual help.
When a user points the mouse cursor to a component and presses the
F1 key, the application fetches the component and sends it a help requests. The request bubbles up through all parent containers until it reaches the component capable of showing help.
When a program has several handlers capable of processing different requests, but you do not know in advance what type of request to expect.
You link several handlers into one chain. Request travels through the chain until it lands on a handler that can process it.
When it is important to execute handlers in a certain order.
Chain of Responsibility allows executing handlers one by one in a given order.
When a set of objects handling requests and their order should change dynamically.
Chain of Responsibility allows altering references withing an existing chain to insert, remove or reorder some handlers.
How to Implement
Handlerinterface with a method for handling request. Decide how will you pass request details into the method. The most flexible way is to convert the request data into an object and pass it to the handling method.
To reduce the duplication of boilerplate code, it might be worth creating an abstract
BaseHandlerclass derived from the Handler interface.
Add a field for storing a reference to a next handler. The field may get initial value from a constructor parameter. You may also define a setter for altering the value of the reference field. But it is required only if you want to be able to modify chains at runtime.
Implement the handling method so that it forwards the request to the next object on the chain if there is one. Concrete handlers will be able to forward requests by calling the parent method. Hence, they will not need access to the reference field, allowing you to make it private.
ConcreteHandlersubclasses and implement their handling methods. Each handler should make two decisions when receiving a request:
- Whether or not it will process the request.
- Whether or not it will pass the request along the chain.
Clients may either assemble chains on their own or receive already constructed chains from other objects. In the latter case, there are factory objects that build chains according to application's configuration or the environment.
Clients may trigger any handler in a chain, not just the first one. The call will be passed along the chain until it reaches the end or until some handler refuses to pass the request further.
Due to the dynamic nature of chain, Client should be ready to handle following scenarios:
- Sometimes a chain may consist of a single link.
- Some requests may not reach the end of a chain.
- Others may reach the chain's end unhandled.
Pros and Cons
- Reduces coupling between senders of requests and their receivers.
- Follows the Single Responsibility Principle.
- Follows the Open/Closed Principle.
- Some requests may end up unhandled.
Relations with Other Patterns
Chain of Responsibility passes a request sequentially along a dynamic chain of potential receivers until one of them handles a request.
Command establishes a one-directional connection from senders to receivers.
Mediator has senders and receivers reference each other indirectly.
Observer passes a request to all concerned receivers at the same time but allows them to subscribe and unsubscribe from receiving further requests dynamically.
But there's another approach, where the request itself is a Command object, passed along a chain of objects. In this case, the same operation may be executed over a lot of different contexts, represented as chained objects.
Chain of Responsibility and Decorator have very common class structures. They both rely on the recursive composition to pass execution through a series of objects. But there are also several crucial differences.
The Chain of Responsibility handlers can execute arbitrary actions, independent of each other. They can also terminate further chaining of the request at will. On the other hand, various Decorators extend a particular behavior and suppose to keep its interface consistent. Also, Decorators are not allowed to break the execution chain at will.