Design Patterns Chain of Responsibility
The Chain of Responsibility pattern passes a request along a line of handlers. Each handler either processes the request or forwards it to the next one. The sender never needs to know which handler ends up doing the work.
The Real-World Analogy
A company support desk sorts questions by difficulty. Front-line agents answer simple questions. Hard ones move to a senior agent, and rare ones reach a manager. The customer makes one call, and the right person responds.
Problem Without a Chain
class SupportDesk {
handle(ticket) {
if ticket.level == 1 → agent code
else if ticket.level == 2 → senior code
else if ticket.level == 3 → manager code
}
}
All routing rules sit in one place. Adding a level means editing this class.
Solution With a Chain
Request ──▶ ┌─────────┐ ──▶ ┌─────────┐ ──▶ ┌─────────┐
│ Agent │ │ Senior │ │ Manager │
│ level 1?│ │ level 2?│ │ level 3?│
└────┬────┘ └────┬────┘ └────┬────┘
│ handled │ handled │ handled
▼ ▼ ▼
Response Response Response
A handler that cannot solve the problem passes it onward. The chain ends when someone handles the request or the line runs out.
Structure
┌─────────────────────────┐
│ Handler │◀─────────┐ next
│ - next: Handler │──────────┘
│ + setNext(Handler) │
│ + handle(request) │
└────────────▲────────────┘
┌───────┴────────┬──────────────┐
┌────┴─────┐ ┌──────┴─────┐ ┌────┴──────┐
│ AgentH │ │ SeniorH │ │ ManagerH │
└──────────┘ └────────────┘ └───────────┘
Roles
- Handler: declares the handle method and keeps a link to the next handler.
- Concrete Handler: decides to act or forward.
- Client: builds the chain and sends the first request.
Code Example
abstract class Handler {
protected Handler next;
Handler setNext(Handler next) {
this.next = next;
return next;
}
void handle(int level) {
if (canHandle(level)) {
process(level);
} else if (next != null) {
next.handle(level); // pass along
} else {
print("No handler available");
}
}
abstract boolean canHandle(int level);
abstract void process(int level);
}
class Agent extends Handler {
boolean canHandle(int level) { return level == 1; }
void process(int level) { print("Agent solved it"); }
}
class Senior extends Handler {
boolean canHandle(int level) { return level == 2; }
void process(int level) { print("Senior solved it"); }
}
class Manager extends Handler {
boolean canHandle(int level) { return level == 3; }
void process(int level) { print("Manager solved it"); }
}
Handler agent = new Agent();
agent.setNext(new Senior()).setNext(new Manager());
agent.handle(2); // Senior solved itTwo Flavors of Chains
A classic chain stops at the first handler that accepts the request. Pipeline variants let every handler act and forward. Web servers use the pipeline style, where each step adds logging, security checks, or compression before the request moves on.
Request ─▶ Logging ─▶ Authentication ─▶ Compression ─▶ Business Logic
Real Projects Using the Chain
| Scenario | Chain Links |
|---|---|
| Web request pipeline | Logging, security, caching, controller |
| Form validation | Required check, format check, range check |
| Expense approval | Team lead, department head, finance |
| Interface events | Button, panel, window |
Advantages and Disadvantages
| Advantages | Disadvantages |
|---|---|
| Sender and receiver stay separate | Requests may reach no handler |
| Handlers reorder or join freely | Long chains slow debugging |
| Each handler owns one rule | Order mistakes cause subtle errors |
Step-by-Step Walkthrough
Follow agent.handle(2) in the code example. The Agent receives level 2 and checks canHandle(). False comes back, and the Agent forwards the call to its next link, the Senior. Accepting level 2, the Senior prints its message. The Manager never sees the request.
Changing the order or removing the Senior takes one line in the setup code. No handler needs an edit.
Designing Good Chains
- Place the most common handlers near the front to save work.
- Keep each handler focused on one rule.
- End the chain with a catch-all handler that reports unhandled requests.
- Document the intended order, since order changes behavior.
Common Mistakes
- Forgetting to forward the request, which silently drops it.
- Building circular chains that loop forever.
- Letting two handlers accept the same request in a first-match chain.
Plain-Words Summary
Chains suit problems where the right handler is unknown at the time of the request. Letting the line of handlers sort things out keeps the sender simple and the rules flexible. Many developers meet this pattern first in web frameworks, where each step of a request pipeline checks one concern. Recognizing the shape makes framework documentation far easier to read.
Key Takeaways
- A chain forwards a request until some handler takes responsibility.
- Senders know only the first handler.
- Handlers join, leave, or reorder without touching client code.
- Always plan for the case where nobody handles the request.
