Card 01/ 08
Chain of Responsibility Pattern: Passing a Request Along Until Someone Handles It
A support ticket goes to the bot, then the agent, then the engineer, and the sender never chooses.
The Chain of Responsibility pattern links handlers in a line, and sends each request to the first one. Each handler either deals with the request or passes it to the next, so the sender never needs to know which handler will answer. This card covers the problem that calls for it, the intent, and when it fits.
The problem is a sender that must know every possible handler. A help desk routes tickets with one function that checks whether the bot can answer, then whether an agent is free, then whether the issue needs an engineer. Each new kind of handler, and each change to the order, is an edit to that function, and the function grows a branch for every handler the business adds.
Without the patternthe sender knows every handler
def route(ticket):
if bot.can_answer(ticket):
return bot.answer(ticket)
if agent.can_answer(ticket):
return agent.answer(ticket)
return engineer.answer(ticket)With the patternthe sender knows the first link
support = Bot(Agent(Engineer()))
support.handle(ticket)Each handler decides for itself whether to answer or pass on.
The Gang of Four state the intent as: avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request; chain the receiving objects and pass the request along the chain until an object handles it.
A support desk that escalates tickets is the familiar picture, and also a real use of the pattern. Each level knows only who to escalate to, and the customer knows only where to send the ticket.
Chain of Responsibility is one of eleven behavioural patterns, the family concerned with which object does which part of the work, and how objects communicate.
| Pattern | What it does |
|---|---|
| Chain of Responsibility (this topic) | Passes a request along a chain of handlers until one handles it. |
| Command | Turns a request into an object that can be stored, queued or undone. |
| Interpreter | Represents a small language's grammar as classes and evaluates its sentences. |
| Iterator | Visits the elements of a collection without exposing how it is stored. |
| Mediator | Routes the communication between objects through one central object. |
| Memento | Captures an object's state so it can be restored later. |
| Observer | Notifies many objects when one object changes. |
| State | Changes an object's behaviour when its internal state changes. |
| Strategy | Swaps one algorithm for another behind a common interface. |
| Template Method | Fixes an algorithm's steps in a base class and lets subclasses fill some in. |
| Visitor | Adds operations to a class hierarchy without changing its classes. |
Use it when
- Several objects could handle a request, and which one should is decided by the request itself.
- The set of handlers or their order changes, or is configured, without changing the sender.
- Each step of processing a request is independent, such as logging, authentication and routing in a web server.
Avoid it when
- There is always exactly one handler, known in advance, so a direct call is clearer.
- Every request must be handled, and nothing guarantees that the chain ends in a handler that accepts everything.
- The chain is long and performance matters, since an unhandled request visits every link.
← → move through sections and cards