Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Chain of Responsibility Pattern
3.
Usage of Chain of Responsibility Pattern
4.
Implementation of Chain of Responsibility Pattern
5.
Advantages of Chain of Responsibility Pattern
6.
Disadvantages of Chain of Responsibility Pattern
7.
Frequently Asked Questions
7.1.
What is an example of a Chain of Responsibility design pattern?
7.2.
What is the Chain of Responsibility pattern responsible for?
7.3.
What is the Chain of Responsibility in OOP?
7.4.
What is the difference between pipeline and Chain of Responsibility?
7.5.
What is the difference between the Observer pattern and the Chain of Responsibility?
7.6.
What is the difference between a Chain of Responsibility and a Strategy pattern?
7.7.
Can the Chain of Responsibility pattern be used with asynchronous programming?
7.8.
Is the Chain of Responsibility pattern suitable for small projects?
8.
Conclusion
Last Updated: Mar 27, 2024
Medium

Chain of Responsibility Design Pattern

Master Python: Predicting weather forecasts
Speaker
Ashwin Goyal
Product Manager @

Introduction

The Chain of Responsibility design pattern is a tool that checks this challenge and ensures requests are handled efficiently.

Chain of Responsibility Design Pattern

Let's see the Chain of Responsibility in more detail.

Chain of Responsibility Pattern

The Chain of Responsibility pattern is a behavioural software design pattern that allows us to pass a request via a chain of handlers until one of them can handle it. Each handler in this chain can run the request or pass it to the next handler in the chain.

This pattern is suitable when we don't know which object in the chain can handle the request and when we want to reduce the coupling between the sender and receiver objects.

UML diagram

The Chain of Responsibility pattern comprises three key components: the Handler abstract class, the ConcreteHandler subclasses, and the Client

  • The Handler abstract class defines the interface for handling requests and maintaining a reference to the next handler in the chain. 
     
  • The ConcreteHandler subclasses implement the handling of specific requests and may pass the request to the next handler in the chain. 
     
  • The Client only sends requests to the first handler in the chain. The above is a UML diagram. To know more about UML, read this blog.
     

For instance, let's consider a scenario where we're developing a web application that validates user input on any website. Create a chain of validators that can handle different types of input validation. For example, we could have a validator that checks for the presence of required fields, another that matches valid email addresses, and a third that reviews for a correct date format.

Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

Usage of Chain of Responsibility Pattern

The Chain of Responsibility pattern is frequently used in a variety of software design settings, such as:

  • Event handling: It is the process of responding to requests or handling events in GUI frameworks or event-driven systems, where a chain of event handlers can process an event.
     
  • Middleware in Web Development: Using the Chain of Responsibility design, middleware components can handle HTTP requests in a pipeline in web frameworks like Express.js for Node.js or ASP.NET Core.
     
  • Logging and error handling: To handle various log levels or error kinds, loggers and error handlers in applications can be chained together.
     
  • Request Handling in Servers: Handling HTTP requests or other network requests in servers or microservices, with each handler having the ability to process or alter the request and pass it on to the following handler.
     
  • Common Handling: Implementation of command handlers in command pattern implementations.
     
  • Authorization and Authentication: A chain of handlers can check user permissions or authenticate requests in security-related actions.
     
  • Data validation and processing: Data validation, transformation, and filtering phases can be implemented in a chain in data processing pipelines.
     

Implementation of Chain of Responsibility Pattern

In this section, we will implement Chain of Responsibility pattern step by step.

Step 1: Define the Abstract Handler Class

class Handler:
   """Abstract handler class"""
   def __init__(self, successor=None):
       self._successor = successor
   def handle_request(self, request):
       if self._successor is not None:
           return self._successor.handle_request(request)

 

Explanation

  • The abstract Handler class defined by the code acts as the parent class for concrete handlers.
  • The __init__ method initializes the handler with an optional successor (the next handler in the chain).
  • In the handle_request method, each handler chooses whether to handle the request directly or recursively pass it on to the following handler.
     

Step 2: Create Concrete Handlers

class ConcreteHandler1(Handler):
   """Concrete handler 1"""
   def handle_request(self, request):
       if request == 'ninja_request_1':
           return "Handled by ConcreteHandler1"
       else:
           return super().handle_request(request)
class ConcreteHandler2(Handler):
   """Concrete handler 2"""
   def handle_request(self, request):
       if request == 'ninja_request_2':
           return "Handled by ConcreteHandler2"
       else:
           return super().handle_request(request)
class ConcreteHandler3(Handler):
   """Concrete handler 3"""
   def handle_request(self, request):
       if request == 'ninja_request_3':
           return "Handled by ConcreteHandler3"
       else:
           return super().handle_request(request)

 

Explanation

  • The Handler base class is the common ancestor of the three concrete handler classes (ConcreteHandler1, ConcreteHandler2, and ConcreteHandler3).
  • To describe its own behavior, each concrete handler overrides the handle_request method. They determine if they can handle a certain request and either call super() or return a corresponding message.To send the request to the following handler in the chain, use handle_request(request).

 

Step 3: Setting Up the Chain of Handlers

if __name__ == '__main__':
   # Setup chain of handlers
   handler1 = ConcreteHandler1()
   handler2 = ConcreteHandler2(handler1)
   handler3 = ConcreteHandler3(handler2)

 

Explanation

  • A chain of handlers is set up in the code's main section. ConcreteHandler1 is constructed with handler1 as its instance. As the successor of handler1, handler2 is constructed as an instance of ConcreteHandler2. Similar to handler2, handler3 is constructed as a ConcreteHandler3 instance with handler2 as its succeeding handler.
  • With this arrangement, a chain is formed, with handler3 at the top, handler2 next, and handler1 at the bottom.
     

Step 4: Sending Requests through the Chain

# Send requests through the chain of handlers
   requests = ['ninja_request_1', 'ninja_request_2', 'ninja_request_3', 'ninja_request_4']
   for request in requests:
       result = handler3.handle_request(request)
       print(f"{request}: {result}")

 

Explanation

  • Using handler3.handle_request(request), a loop iterates through each request in the list and presents it to the handler at the top of the chain (handler3).
  • Each request processing result is printed, indicating which handler processed the request based on the content of the request.
     

Advantages of Chain of Responsibility Pattern

Let’s see some advantages of the Chain of Responsibility:

  • Flexible and Dynamic: The Chain of Responsibility pattern allows for a relaxed and dynamic approach to handling requests. New handlers can be added or removed from the chain without affecting existing ones.
     
  • Decoupling: It helps to decouple the client code from the handling logic. The client code only needs to know about the first handler in the chain and doesn't need to know about the other handlers.
     
  • Single Responsibility: Each handler in the chain has a single responsibility. This makes the code easier to maintain and understand.
     
  • Easy Testing: It makes testing each handler in isolation accessible. Each handler has a clearly defined responsibility and can be tested independently.

Disadvantages of Chain of Responsibility Pattern

Let’s see some disadvantages of the Chain of Responsibility:

  • Performance: The Chain of Responsibility pattern can be less efficient than other patterns, as it requires each handler to be called sequentially until a handler can be found to handle the request. This can lead to performance issues if the chain is very long.
     
  • Overhead: It can introduce additional overhead, as each handler needs to check whether it can handle the request.
     
  • Complexity: It can add complexity to the code, especially if the chain is long or has many handlers.
     
  • Order Dependency: The order of the handlers in the chain can be significant, as it determines which handler will handle the request. This can lead to order dependency issues, which can be challenging to debug.

Frequently Asked Questions

What is an example of a Chain of Responsibility design pattern?

An example is processing of email messages where messages are routed through handlers based on their type or content.

What is the Chain of Responsibility pattern responsible for?

According to the Chain of Responsibility design pattern, request is passed along a chain of handlers, and each handler has the choice of processing the request or passing it on to the following handler.

What is the Chain of Responsibility in OOP?

The Chain of Responsibility is a behavioural design pattern used in object-oriented programming (OOP), which enables flexible request processing by passing a request via a series of handlers.

What is the difference between pipeline and Chain of Responsibility?

The main difference is that while Chain of Responsibility creates a dynamic chain where one handler may transmit requests to the next, a pipeline is a linear sequence of processing stages.

What is the difference between the Observer pattern and the Chain of Responsibility?

The Chain of Responsibility handles a series of requests by passing them through a chain of handlers until one of the handlers can handle the request. Now, the Observer pattern is used to notify a group of objects about a change in the state of a subject-object.

What is the difference between a Chain of Responsibility and a Strategy pattern?

The chain of responsibility pattern involves passing requests to possible receivers, while the command pattern employs an encapsulated request within a command object..

Can the Chain of Responsibility pattern be used with asynchronous programming?

Yes, the Chain of Responsibility pattern can be used with asynchronous programming. But it introduces the potential for concurrency issues, which can be challenging to handle in a chain of handlers. It is recommended to use a thread-safe implementation with appropriate locking mechanisms to handle concurrency issues.

Is the Chain of Responsibility pattern suitable for small projects?

It can be used in small projects but may be overkill for straightforward scenarios. It is most suitable for larger projects with complex request-handling requirements.

Conclusion

In this article, we discussed the Chain of Responsibility Design Pattern in detail. We also saw its implementation in Python, some real-life examples and the difference between the Chain of Responsibility and other design patterns. 

If this article helped, check out our other related articles:

You can also consider our System Design Course to give your career an edge over others.

Happy Learning, Ninjas!

Previous article
Activity Diagram in System Design
Next article
What are SOLID Principles in Low Level System Design?
Live masterclass