6.9. Chain of Responsibility

  • EN: Chain of Responsibility

  • PL: Łańcuch zobowiązań

  • Type: object

The Chain of Responsibility design pattern is a behavioral design pattern that allows an object to pass the request along the chain of potential handlers until an object handles it. It simplifies your code and allows you to skip explicit sender-receiver bindings.

In Python, we can implement the Chain of Responsibility pattern using classes. Here's a simple example:

First, we define a Handler class that declares an abstract handle method and a method to set the next handler:

>>> class Handler:
...     def __init__(self):
...         self._next_handler = None
...     def set_next(self, handler):
...         self._next_handler = handler
...         return handler
...     def handle(self, request):
...         if self._next_handler:
...             return self._next_handler.handle(request)
...         return None

Then, we define a ConcreteHandler class that implements the handle method:

>>> class ConcreteHandler1(Handler):
...     def handle(self, request):
...         if request == "request1":
...             return "Handler1"
...         else:
...             return super().handle(request)

Finally, we can use the Handler and ConcreteHandler classes like this:

>>> handler1 = ConcreteHandler1()
>>> handler2 = ConcreteHandler1()
>>> handler1.set_next(handler2)  
<__main__.ConcreteHandler1 object at 0x...>
>>> print(handler1.handle("request1"))
>>> print(handler1.handle("request2"))

In this example, the ConcreteHandler1 handles the request if it can, otherwise it passes the request to the next handler in the chain.

6.9.1. Pattern

  • Chain of objects

  • Create a pipeline of classes with different responsibilities

  • Open/Close Principle for adding new handlers


6.9.2. Problem


6.9.3. Solution


from abc import ABC, abstractmethod
from dataclasses import dataclass

class HttpRequests:
    username: str
    password: str

    def get_username(self) -> str:
        return self.username

    def get_password(self) -> str:
        return self.password

class Handler(ABC):
    next: 'Handler'

    def do_handle(self, request: HttpRequests) -> bool:

    def handle(self, request: HttpRequests) -> None:
        if self.do_handle(request):
        if self.next:

class Authenticator(Handler):
    def do_handle(self, request: HttpRequests) -> bool:
        is_valid: bool = (request.get_username() == 'admin' and
                          request.get_password() == 'myVoiceIsMyPassword')
        return not is_valid

class Compressor(Handler):
    def do_handle(self, request: HttpRequests) -> bool:

class Logger(Handler):
    def do_handle(self, request: HttpRequests) -> bool:

class WebServer:
    handler: Handler

    def handle(self, request: HttpRequests) -> None:

if __name__ == '__main__':
    # authenticator -> logger -> compressor
    compressor: Compressor = Compressor(None)
    logger: Logger = Logger(compressor)
    authenticator: Authenticator = Authenticator(logger)
    server = WebServer(authenticator)
    server.handle(HttpRequests('admin', 'myVoiceIsMyPassword'))

6.9.4. Assignments

  • Add Encryptor handler

  • Make pipeline: authenticator -> logger -> compressor -> encryptor