Chain of Responsibility Design Pattern
Overview
The Chain of Responsibility Design Pattern is a Behavioral Design Pattern. Behavioural patterns are those design patterns that describe and identify the interactions between different objects. It is also known as the Chain of command Design Pattern. It makes use of chain handler objects to handle a request generated by a client.
When Will We Need the Chain of Responsibility Design Pattern?
Say you are writing some code for the software you are creating, however, you find yourself in a situation where you're aware of some functionality that is required to be performed, but you're unaware of who specifically is going to perform that task. It is at this point, that you can make use of the Chain of Responsibility design pattern. This behavioral pattern allows request clients to send their requests without being aware of how they will be received and handled finally. This design pattern will allow an object to send commands to other objects without having the knowledge of which object would handle it upon receiving it. So basically, you can think of this as a delegation of tasks by a leader, except, the leader does only the task of giving out tasks, and is unaware of who is doing it. This way, the tasks are divided and done efficiently by the whole system
In other terms, if one object generates an event that has to be handled by another object (which is a part of a chain of objects performing similar tasks), then a worker object would pass the event along the chain that consists of commands and objects, leading to the task being performed finally by another object that can execute it.
A real-life example of the Chain of Responsibility design pattern is the ATM Machine, we enter our ATM card and the Chain of Responsibility is implemented by the machine, finally giving us cash. Let's look at it in more detail below:
Another way of looking at it:
Or a more technical representation:
Here we have an object that creates a request (represented by the first box in the diagram above). We then see that it is given to a handler of a request which then passes on to another handler (chain of handlers in red dashed box), and finally to the ordering system or interface which completes the request hence creating a Chain of Responsibility. We pass on the request to multiple handlers as each handler has a different responsibility, a different task to perform before the final result is given.
Formally, the intent of using the Chain of Responsibility design pattern is:
- Steer clear of coupling (joining) the sender of a request to the receiver of said request, by allowing more than one object an opportunity to deal with the request. The receiving objects are chained and the request is passed along this chain until it is handled by an object.
- Launch requests, and pass them on to the chain that contains many possible handlers of the request.
- Creation of an object-oriented linked list that has recursive traversal.
How Does the Chain of Responsibility Pattern Work?
Now as you saw above, we have a problem that works with a chain that passes on responsibility to other classes, and of course, this is a problem that will be solved by the chain of a responsibility design pattern as we will be creating a chain that gives responsibilities to different objects.
We'll discuss this in detail below, but here's what the most basic structure of a Chain of Responsibility looks like.
Structure
As you can see in the structure, we have 3 main components - handler, client, and concrete handlers. The client is where the request originates and then this is accessed by the handler in the final stage. The concrete handlers are the actual handlers of the request that are chained in some sequential order as we saw in the diagram depicting the technical representation. They are the objects that have the final responsibility of handling the request. And the last object, the handler can be considered as an interface that will receive the request primarily from the client and it then dispatches the request to the chain of the handlers. It only has a reference to the first handler in the chain and has no information about the rest of the handlers.
Now let's try implementing this Chain of Responsibility step by step by using the ATM machine example discussed earlier.
Implementation Details & Pseudocode
First, we'll implement the pseudocode of the Handler class. Since handlers are a part of the chain, it is obvious that they would have a successor. And, it would also have a method called "Withdraw" which would withdraw a certain amount of money from the ATM of that handler's denomination. This would take as input an object - account of the BankAccount class which we will create next.
Post this handler class, we must make a BankAccount class to create the account object. Since we're working with an ATM machine, the details that we would want to implement are - the amount requested by the user of the ATM machine, the total amount in their bank account, the account number, and the remaining amount to be issued to the user in notes. Along with that, we would have some get functions to get the above amounts.
Now that we have created the handler class, as well as the BankAccount class, we next need to create the specific handlers using the handler class for specific denominations - hundred, five hundred, and thousand.
All these classes would extend the handler class since the responsibility will be passed on from handler to handler. A thousand handlers, extending the handler class will only handle the notes when the amount that is requested by the user is above 1000, needless to say. It will then check the number of notes required. After calculating the number of 1000 notes required, if there is any amount left, the responsibility of the remaining amount will be passed on to the 500 handlers.
In the same way, we will create handler classes for five hundred and hundred. Post that, we will write our main program, in which instances of the concrete handlers (receiver) classes will be created. This is when we set the successors, and the chain is created. The chaining in the main program can be changed without making any changes to the logic. Let's take a look at the pseudocode:
Let's now look at the pros and cons of the Chain of Responsibility design pattern.
Pros and Cons of Chain of Responsibility Design Pattern
Pros:
- The order of request handling can be controlled by you.
- Classes that invoke the operations can be decoupled (separated) from the classes that are performing the operations. This is called the single responsibility principle.
- New handlers can be introduced into the application without having to break the existing code.
Cons:
- It may happen that some requests may end up being left unhandled.
- The performance of our system will be affected, however debugging this code is not easy as it may result in a cyclic call (objects calling other objects in a cyclic manner).
Difference between the Chain of Responsibility and other Patterns
To completely understand the Chain of Responsibility design pattern, let's see how it is different from other similar design patterns.
1. Difference between command pattern and Chain of Responsibility design pattern:
The main difference between these two patterns is the way in which the request generated by the client is decoupled. In the command pattern, we have a command object which is used for encapsulating the request. However, here as you read, the request from the client is sent passed on through a chain to potential receivers. We have multiple handler classes at different levels in the Chain of Responsibility, but in the command design pattern, we have command classes and receiver classes. In the Chain of Responsibility design pattern, the client calls the handler objects, however in the command design pattern the client calls the receiver objects directly.
2. Difference between Using the Chain of Responsibility Design Pattern and a Simple if-else-if Block of Code:
Although the implementation of simple programs can be done using basic if-else-if blocks, let's take a look at this scenario: There is a window on your screen which contains a panel. This panel has a text box inside it. Once you right-click the text box, whatever command is executed, will depend on the hierarchy of the system. For example, the system will first give this responsibility to the first handler which is the text box. If the text box does not know what to do with the request, the request will be passed on to its parent by the text box - the panel. This kind of scenario cannot be designed using if-else blocks since every change in the UI would require change in the code. So if you decide that there should be another container that has say, a button or another text box, then you would have to change the complete code according to this new UI. The "chain" of responsibility of handling the request created by the user would change.
To prevent this, making the use of handler objects would make the code exchangeable and re-usable. If you have your panel represented by a class that extends the handler class, followed by another class textbox, you will be able to implement a chain. Now if you add another container, you simply have to create a new class that extends the "handler" class much like the text box and panel classes.
FAQs
Q: Is the chain of responsibility design pattern used with another similar pattern?
A: This design pattern is very often used with the Composite design pattern. Upon usage in conjunction with the composite pattern, when leaf components get requests, the request may pass through a chain of all the present parent components to the root of the object tree.