Introduction
The Chain of Responsibility pattern describes a chain of command/request receivers. The client has no idea which one of the receivers will handle the request. The beauty of the pattern is again uncoupling: the sender and the receiver do not know about each other. A danger is that no receiver in the chain will handle the request, this has to be taken into account when implementing this pattern.
An excellent example of this could be the way that middleware is implemented in some web-frameworks (Gin, but also ASP.NET core are examples).
It looks like this:

Let us break this down:
- We have a Client who does a request to a Handler
- The Handler has a list of Receivers, and succesively sends the request to a receiver until one handles the request
As you can see, this is not a very complicated pattern
Implementation in Python
Prerequisites
Since we will implement a class that will be referring to itself, we need the following opening line:
from __future__ import annotations
from abc import ABC, abstractmethod
Line by line:
- In the
Handlerwe need to be able to refer to the class itself, so we need to importannotations. To be precise, this import enables PEP 563, postponed evaluation of annotation. This allows forward references without quotes. - The
Handlerclass will be an abstract base class, that is why theABCis needed. Also, thehandle()method will be implemented differently in each subclass, so we need the@abstractmethoddecorator.
The Handler base class
We will start by defining the Handler base class:
class Handler(ABC):
def __init__(self, next_handler: Handler | None = None):
self._next_handler = next_handler
def set_next(self, handler: Handler) -> Handler:
self._next_handler = handler
return handler
@abstractmethod
def handle(self, request: str) -> str | None:
pass
def _handle_next(self, request: str) -> str | None:
if self._next_handler:
return self._next_handler.handle(request)
return None
def __str__(self) -> str:
return self.__class__.__name__
Line by line:
- In the constructor, the next handler in the chain can be set. Note that if no handler is supplied the default value is
None - When you can not set the next handler, change it, or remove it, this is what the
set_next()method does. This method returns the handler supplied in the parameter list, as this allows method chaining. - handle() is used to perform the actual action. Because this will be handled differently by different subclasses of the
Handlerclass, this method has been decorated with the@abstractmethoddecorator. This method returns either a string orNonedepending on the result of handling the request. - At the end of each implementation, if we want the chain to function properly we need to call the
_handle_next()method which calls the next handler in the chain, and returns the result. If no handler is available it returnsNone. - The
__str__method returns some basic class information.
Implementing the handlers
Next we define the first handler imaginatively called HandlerA:
class HandlerA(Handler):
def handle(self, request: str) -> str | None:
if request == "A":
return f"{self}: processed the request."
return self._handle_next(request)
A short breakdown:
HandlerAis a subclass ofHandler. The only method we need to implement is thehandle()method, which handles the specific handling of the requests.- In the method we check if we need to handle the request, i.e. if the request is the character A. If so, we handle it, and the chain ends.
- If not, we call the next handler in the chain, if there is one.
Next we define two other handlers which work similarly:
class HandlerB(Handler):
def handle(self, request: str) -> str | None:
if request == "B":
return f"{self}: processed the request."
return self._handle_next(request)
class HandlerC(Handler):
def handle(self, request: str) -> str | None:
if request == "C":
return f"{self}: processed the request."
return self._handle_next(request)
Finally we create a default handler which is at the end of the chain. This simply returns an error message:
class DefaultHandler(Handler):
def handle(self, request: str) -> str | None:
return f"No handler found for request: '{request}'"
Creating the chain
Finally we create a small function to create this handler chain:
def create_handler_chain() -> Handler:
handler_a = HandlerA()
handler_b = HandlerB()
handler_c = HandlerC()
default_handler = DefaultHandler()
handler_a.set_next(handler_b).set_next(handler_c).set_next(default_handler)
return handler_a
Some remarks:
- We start by creating four handlers, the last one being the
DefaultHandler. - Next we set
handler_bas next in the chain fromhandler_aand so, with the last handler in the chain being thedefault_handler - All we need to do now is return
handler_awhich is the head of the chain.
Time to test
Now we can see whether our setup works:
if __name__ == "__main__":
handler_chain = create_handler_chain()
test_requests = ["A", "B", "C", "D", "E"]
print("Chain of Responsibility Pattern Demo")
print("=" * 40)
for request in test_requests:
result = handler_chain.handle(request)
print(f"Request '{request}': {result}")
Line by line:
- We start by creating the chain.
- Next we set up a list of possible request. Note that we have no handlers for a D or an E request as we shall see.
- We iterate over the list, and call
handle()on the chain. Three of the five requests will be handles, the last will be handled by the default handler.
Conclusion
The Chain of Responsibility pattern offers a powerful way to decouple senders from receivers, promoting flexibility and maintainability in your code. As demonstrated, its implementation in Python is straightforward, relying on an abstract Handler class and concrete subclasses that decide whether to process a request or pass it along the chain. This pattern is particularly valuable in scenarios like middleware pipelines, where requests flow through a series of handlers until one takes action. While careful consideration is needed to prevent requests from being unhandled, the Chain of Responsibility effectively simplifies complex conditional logic by distributing responsibilities among a clear, sequential structure of objects.




