Understanding Chain of Responsibility Pattern

Jalitha Dewapura
Design Patterns with Java
5 min readMay 28, 2021

--

The chain of responsibility pattern is under the behavioral design patterns. As the name implies, this pattern can handle a few different activities as linkages of a chain. Each activity receives the next activity to make the chain. The receiver can perform the activity without knowing the sender(previous process) and the sender also performs its job without knowing the receiver(next process). It means this pattern can achieve loosely coupled software design for our application. This set of activities can be considered as a pipeline or a chain of objects for processing a request.

Let’s assume we need to implement a system to complete three activities called A, B, and C. These activities should be completed in the correct order and each has to perform independently. This can be achieved by the chain of responsibility pattern.

Explanation of Chain of responsibility pattern with a real-world example

Let’s imaging we want to build a web server. I will not confuse you with getting too much technical about how web servers work. At the minimum, we need to deal with the concept of an HTTP request. An HTTP request is a request that is sent from a client like your web browser to a web server. So, we need to handle this request. As part of handling a request, several activities need to perform, such as Authentication, Logging, Compression, etc.

Traditional Approach

First, we need to create HttpRequest class. To simplify this example, I made it with only two properties such as username and password.

HttpRequest class

Then we need to create separate classes for each action. Otherwise, it will violate a very important principle in object-oriented design that is called the single responsibility principle. It basically means, every class should have a single responsibility.

Authenticator class

This class is purely responsible for authenticating a request to make sure this request is sent by a valid user. For this example, I don’t want to involve with the database. It is just to check if the username is equal to ‘admin’ and the password is equal to ‘password123’.

Logger class

This class is responsible for logging the user. (Not implemented. I just put a print statement to show the method).

Compression class

This class is responsible for compressing the request. (Not implemented. I just put a print statement to show the method).

Then we need to create the WebServer class to handle these activities.

WebServer class

It has the handle() method to handle the request as an argument and pass it to different activities. This class is responsible for maintaining the correct order of activities.

Then the client can access this handle() method in WebServer class.

Application class

Here I created a WebServer instance and called the handle() method. All the activities have been called in particular order inside this method.

If in the future, we want to disable logging for certain scenarios, we cannot do. We have to come back and change the code in the WebServer class. Similarly, if you want to add another step like Encryption, we have to modify the code in the WebServer class. This violates the open-closed principle. It means the system should be open for extension and closed for modifications. This is the problem, the chain of responsibility pattern solves.

Chain of Responsibility Pattern Approach

In this approach, I introduce the Handle class that links each activity. This class is abstract. Because there is an abstract method called doHandle. All the activities must be inherited from this class and implement the activity inside this method.

When the activity is inherited from the Handler class, all activities must have a custom constructor with calls next activity.

doHandler() method — The process of the activity

Authenticator class

All activities take one argument which will be the next activity.

Logger class

If this activity is not the end of the process, the doHandler() method should return false. If it returns true, the process will be terminated. Look at the Authenticator class. It returns the inverse of the isValid property. It means the user is valid, it returns false.

Compressor class,

WebServer class,

This WebServer class is responsible for the start of the process. It means the firstHandler keeps the instance of an Authenticator class. It is taken as the constructor argument. The start() method should be called to start the process.

The HttpRequest class remains the same as the previous one. Let’s see how to client works with this approach.

Application class,

First, the client needs to create instances from all the required activities. Each activity takes the next activity instance as an argument.

The Authenticator takes logger instance.

The Logger takes Compressor instance.

The Compressor is the endpoint of the process. So, it takes a null argument.

Next, the client needs to create a WebServer instance by passing the authenticator instance as an argument. Then he should start the server.

Output,

After sometimes, the client may need to remove the Logger activity. He just needs to remove the link between the authenticator and the logger. Then set it between the authenticator and the compressor.

Application class,

Output,

If the client wants to add one more activity to this process, it will be really easy. Assume, he needs to add Encryption activity to this process.

Encryptor class

The doHandle() method must be implemented.

Application class,

Then the client should link activities.

Authentication → Compression → Encryption

Output,

Now you can realize the beauty of the Chain of responsibility pattern and how it works with the open-closed principle.

The only disadvantage of this pattern is the client can change the process steps. If the order of activity should be fixed, it needs to implement in a separate class.

Here it comes to the end of this article. I hope you understand the importance of the Chain of responsibility pattern. If I miss some points, let me know in the comment section. Keep in touch. I will post the design patterns as well.

Reference

[1] Richard Helm, Ralph Johnson, John Vlissides, Erich Gamma, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994.

--

--