Overview
Chain of Responsibility passes a request along a chain of handlers; each handler either processes it, passes it on, or both. The sender does not know which handler will act. Handlers can be added, removed, or reordered without changing the sender.
It is the pattern behind middleware pipelines (auth, rate limit, logging, validation), approval workflows (manager, director, VP approve by amount), support escalation, and ATM cash dispensing by denomination. Each handler has one responsibility, and the chain composes them.
Your question goes to a chatbot first, then a support agent, then a specialist, then a manager. Each one either solves it or passes it up. You do not need to know who will ultimately answer.
When to use it
- Several objects may handle a request, decided at runtime.
- A pipeline of checks or processing steps.
- Escalation by level or amount.
Where it shows up in interviews
Recognize it when: approval depends on amount or level.
- Design an expense approval system
- Design a loan approval workflow
Recognize it when: ordered checks before handling.
- Design an API middleware pipeline
- Design a logging framework with levels
Recognize it when: split an amount across handlers.
- Design an ATM cash dispenser
- Design a vending machine change maker
Where it is used in real software
Each middleware calls next() to pass control down the chain.
Security filter chains handle authentication and authorization in order.
Loggers pass events up to parent loggers and appenders.
Key terms
- Handler
- Processes or forwards a request.
- Successor / next
- The next handler in the chain.
- Short-circuit
- Stop the chain (reject or fully handle).
- Pipeline
- Every handler processes and passes on.
How it works, step by step
- 1Define the handler interface
handle(request) with a next reference.
- 2Implement one concern per handler
Auth, rate limit, validation.
- 3Decide: handle, pass, or stop
Each handler chooses.
- 4Assemble the chain
Order matters; build in one place.
- 5Handle the end of the chain
Default handler or error if unhandled.
STEP 1380 / 100 = 3 notes. Remaining 80 passed on.
Expense approval chain
TeamLead approves <= 1,000; Manager <= 10,000; Director <= 50,000; else CFO
| Expense | TeamLead | Manager | Director | Approved by |
|---|---|---|---|---|
| $300 | Approves | - | - | TeamLead |
| $4,500 | Passes | Approves | - | Manager |
| $22,000 | Passes | Passes | Approves | Director |
| $80,000 | Passes | Passes | Passes | CFO |
NOWExpense: $300 | TeamLead: Approves | Manager: - | Director: - | Approved by: TeamLead
Changing limits or adding a level edits the chain assembly, not the request code.
Implementation
public abstract class CashHandler { private CashHandler next; private final int denomination; private int available; protected CashHandler(int denomination, int available) { this.denomination = denomination; this.available = available; } public CashHandler then(CashHandler next) { this.next = next; return next; } public void dispense(int amount, Map<Integer, Integer> out) { int notes = Math.min(amount / denomination, available); if (notes > 0) { out.put(denomination, notes); available -= notes; amount -= notes * denomination; } if (amount == 0) return; if (next == null) throw new IllegalStateException("Cannot dispense remaining " + amount); next.dispense(amount, out); }} final class Hundreds extends CashHandler { Hundreds(int n) { super(100, n); } }final class Fifties extends CashHandler { Fifties(int n) { super(50, n); } }final class Twenties extends CashHandler { Twenties(int n) { super(20, n); } }final class Tens extends CashHandler { Tens(int n) { super(10, n); } } CashHandler chain = new Hundreds(10);chain.then(new Fifties(10)).then(new Twenties(20)).then(new Tens(20));Map<Integer, Integer> notes = new LinkedHashMap<>();chain.dispense(380, notes); // {100=3, 50=1, 20=1, 10=1}Complexity and performance
Worst case passes through all.
Insert into chain.
Trade-offs
A request may fall off the end unhandled; add a default handler.
Following a request through many handlers needs logging or tracing.
Variants and related techniques
Middleware where every handler processes and passes on.
Stop at the first handler that can handle.
Common mistakes
- Order-dependent bugs.
Fix: Build the chain in one place and test the order (auth before rate limit, etc.).
- Handlers doing multiple jobs.
Fix: One concern per handler.
Interview questions
How would you design an ATM's cash dispenser?
Chain of Responsibility: a handler per denomination from largest to smallest; each dispenses as many notes as possible within its stock and passes the remainder on; if the last handler cannot finish, reject and roll back.
Chain of Responsibility vs Decorator?
Both link objects. Decorators always delegate and add behavior around the call; chain handlers may stop the request and decide whether to pass it on.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Expense approval chain | Easy | Escalation. |
| ATM dispenser with limited notes | Medium | Rollback on failure. |