Chain of Responsibility
Pass a request along a line of handler objects; each one decides whether to handle it, pass it on, or stop the chain dead.
The problem
You are building an ordering system, and orders should only come from authenticated users. Then admins need full access, so that is a permissions check. Then a colleague points out that raw request data should be sanitized. Then someone notices the login endpoint is a brute-force buffet, so you add IP throttling. Then someone wants caching, which is another check in front of everything.
The checks have to run in order, and each one has veto power - failed authentication makes every later check pointless. So they pile up in one method.
Now protect a second endpoint that needs three of those five checks in a different order. You cannot. You copy them, and from that moment the two copies drift.
The solution
Turn each behavior into a standalone object - a handler - with a single method that takes the request. Then link the handlers: every handler holds a reference to the next one. A request enters the chain and travels along it, and at each link the handler makes two independent decisions: do I process this, and do I pass it on.
That second decision is the interesting one. A handler is allowed to stop the chain, which is what makes CoR more than a fancy loop. In the canonical GUI variant, the first element capable of handling the event consumes it and nothing downstream ever hears about it.
The one hard requirement: all handlers implement the same interface, and each knows nothing about the next link beyond that interface. That is what lets you compose chains at runtime out of whatever handlers the situation calls for.
- 1The user hits F1. The app finds whatever sits under the cursor - here, the Cancel button - and sends the help request there.
- 2The request enters the chain at the deepest element. The app has no idea how far it will travel, and does not need to.
- 3Decision one: can I handle this? Cancel was never given tooltip text, so the answer is no.
- 4Decision two: do I forward? Yes - the button hands off to its container and drops out of the story entirely.
- 5The panel does have modal help text, so it handles the request its own way: a modal window instead of a tooltip.
- 6And here it stops. The dialog never hears about the request - a handler that processes a request is free to end the chain.
Structure
The GUI example is a nice reminder that a chain can be lifted straight out of an object tree: a component's chain is just its containment path to the root. Note the optional base handler - the class that owns the next-link field and the default "forward it" behavior so concrete handlers do not each reinvent it.
Code
Same example three ways: contextual help bubbling from a widget up through its containers.
When to use it
- Your program handles several kinds of request in several ways, and you do not know the types or their order up front.
- Order of execution matters and should be explicit. A chain makes the order literal data rather than the shape of an if-else ladder.
- The set of handlers, or their order, must change at runtime. Give the next-link field a setter and you can insert, remove, and reshuffle handlers on the fly.
Pitfalls
- Requests can vanish. Nothing guarantees a taker, so a request may fall off the end of the chain with no answer. Decide up front whether that is acceptable or whether the last link should be a catch-all.
- Debugging by flashlight. A bug becomes "which of these eleven handlers ate my request", and the answer is only visible at runtime. Logging at each hop is cheap insurance.
- Chains of one. The client should survive a single-link chain, a request handled at the first hop, and a request handled at none of them. All three are normal.
Don't confuse it with
- Decorator. The class diagrams are nearly twins - both use recursive composition to push execution through a series of objects. The difference is what each is permitted to do. A decorator must keep the request flowing and stay behaviorally compatible with the interface it wraps; a CoR handler may run something entirely unrelated and stop the chain whenever it likes.
- Command. A CoR handler can be implemented as a Command, and the request travelling the chain can itself be a Command - the patterns compose happily. Their intents differ: CoR is about finding a receiver, Command is about packaging a request.
- Composite. They pair naturally. A leaf that receives a request can pass it up through its parents to the root, which is exactly the contextual-help example.
- Middleware stacks. Express, Rack and friends are CoR with a job title: each
middleware handles the request, calls
next(), or short-circuits with a response.
Check yourself
When a handler receives a request, how many decisions does it make?