Chain of Responsibility Pattern

The Chain of Responsibility is a behavioral design pattern used in software development to process a request through a chain of handlers. Each handler in the chain either processes the request or passes it to the next handler. This pattern promotes loose coupling and flexibility by allowing an object to send a request without knowing which object will handle it. [1]

Here’s an overview of how the Chain of Responsibility pattern works:

  1. Handler Interface (or Abstract Class): Defines an interface for handling requests and optionally contains a reference to the next handler in the chain.
  2. Concrete Handlers: Implement the handler interface and process specific types of requests. Each concrete handler decides whether to handle the request or pass it along the chain.
  3. Client: Initiates the request and relies on the chain to handle it.

Key Components

Example

Here's an example in Python:

from abc import ABC, abstractmethod

class Handler(ABC):
    def __init__(self, successor=None):
        self._successor = successor
    
    @abstractmethod
    def handle_request(self, request):
        pass

class ConcreteHandler1(Handler):
    def handle_request(self, request):
        if 0 <= request < 10:
            print(f"ConcreteHandler1 handled request {request}")
        elif self._successor:
            self._successor.handle_request(request)

class ConcreteHandler2(Handler):
    def handle_request(self, request):
        if 10 <= request < 20:
            print(f"ConcreteHandler2 handled request {request}")
        elif self._successor:
            self._successor.handle_request(request)

class ConcreteHandler3(handler.Handler):
    def handle_request(self, request):
        if 20 <= request < 30:
            print(f"ConcreteHandler3 handled request {request}")
        elif self._successor:
            self._successor.handle_request(request)

# Client code
if __name__ == "__main__":
    # Create handlers
    h3 = ConcreteHandler3()
    h2 = ConcreteHandler2(h3)
    h1 = ConcreteHandler1(h2)
    
    # Send requests
    requests = [5, 14, 22, 18]
    
    for req in requests:
        h1.handle_request(req)

Advantages

Disadvantages

Use Cases

The Chain of Responsibility pattern is ideal for scenarios where:

Real-World Examples

  1. Technical Support Systems: Customer support queries might be handled by different levels of support staff based on the complexity and type of query.
  2. Logging Systems: Different loggers (e.g., console logger, file logger, remote server logger) might handle log messages based on their severity level or type.
  3. Event Handling Systems: GUI frameworks often use this pattern to manage event handling where events are passed through a chain of handlers until one processes it.

Enhancing the Example

To make the example more robust and illustrate handling unprocessed requests, you could add a default handler at the end of the chain:

class DefaultHandler(Handler):
    def handle_request(self, request):
        print(f"Request {request} was not handled")

# Client code
if __name__ == "__main__":
    # Create handlers with a default handler at the end
    default_handler = DefaultHandler()
    h3 = ConcreteHandler3(default_handler)
    h2 = ConcreteHandler2(h3)
    h1 = ConcreteHandler1(h2)
    
    # Send requests
    requests = [5, 14, 22, 18, 99]
    
    for req in requests:
        h1.handle_request(req)

In this enhanced version:

This pattern provides a flexible approach to processing requests by promoting loose coupling between request senders and receivers. By understanding its advantages and limitations, you can effectively apply it to suitable scenarios in your software design.


  1. https://refactoring.guru/design-patterns/chain-of-responsibility ↩︎