creational
Chain of Responsibility
Pass a request along a chain of handlers. Each decides to process or pass to the next.
A request arrives and something must handle it, but which handler depends on the request.
An expense claim might be approved by a manager, a director or the CFO depending on the amount. A web request passes through authentication, rate limiting and logging.
Chain of Responsibility links the handlers together. Each one either deals with the request or passes it to the next. The sender just hands it to the first link.
Customer support escalation — Level 1 handles simple issues, escalates to Level 2, then engineering.
Key Concepts
1
Each handler has a reference to the next one in the chain.
2
When a request arrives, the handler decides whether it can deal with it. If yes, it handles it and usually stops there. If not, it passes it along.
3
The sender only knows the first handler. It has no idea how long the chain is or who eventually responds.
4
Two things to decide up front. What happens if nobody handles the request, since silently doing nothing is hard to debug. And be aware the behaviour depends on the order of the chain, which is set in configuration rather than visible in any one handler.
When to use it
- HTTP middleware pipelines
- Event bubbling in DOM
- Logging frameworks
- Approval workflows
Watch out for
- A request can fall off the end of the chain unhandled, and silent no-ops are painful to debug — decide explicitly whether that is an error
- Behaviour depends on chain ORDER, which is configuration rather than code, so the bug is often in the wiring and invisible in any single handler
- Long chains cost a call per link and make stack traces deep; a Map lookup is better when the correct handler is known up front
java
public abstract class Handler {
private Handler next;
public Handler setNext(Handler n) { this.next = n; return n; }
public void handle(Request req) {
if (next != null) next.handle(req);
}
}
public class AuthHandler extends Handler {
public void handle(Request req) {
if (req.getHeader("Auth") == null) throw new UnauthorizedException();
super.handle(req);
}
}
// Assemble
Handler chain = new AuthHandler();
chain.setNext(new RateLimitHandler()).setNext(new LoggingHandler());