3.1 Chain of Responsibility
The Chain of Responsibility pattern lets you pass a request along a line of handlers, each of which either handles it or forwards it to the next. The sender never knows who ultimately handled it — it just drops the request at the head of the chain.
Expense approval is a perfect example: a team lead can approve small sums, but bigger amounts escalate upward until someone is "authorized enough." Drag the amount in the lab below and watch the request flow up the approval chain until a level approves it.
Each handler knows only its successor
abstract class Handler {
protected Handler next;
public Handler setNext(Handler n) { this.next = n; return n; }
public void handle(Request req) {
if (canHandle(req)) process(req);
else if (next != null) next.handle(req); // pass to the next
}
protected abstract boolean canHandle(Request req);
protected abstract void process(Request req);
}Each handler holds just one next reference. The chain's structure (who comes before whom, how many links) can be assembled flexibly, even adjusted at runtime.
Any link can short-circuit the whole chain
Web-framework middleware is the real-world incarnation of CoR: auth, rate-limit, and logging process the request layer by layer, and any layer can intercept and stop the rest (e.g., auth failure returns 401 immediately). The lab below lets you reorder middleware and toggle the token, watching where the request gets blocked.
Tradeoffs
- Benefits: sender and receiver are decoupled; handlers can be added, removed, and reordered independently; each handler has a single responsibility.
- Risks: a request may traverse the entire chain unhandled (you need a fallback); a long chain makes "who actually handled it?" hard to trace during debugging.