Back to All Design Patterns
Pattern 12 of 22
Behavioral PatternINTERMEDIATE7 min deep dive
Chain of Responsibility Pattern
Passes requests along a chain of handlers. Upon receiving a request, each handler decides either to process it or to pass it to the next handler in the chain.
Real-World Mental Model / Metaphor:
A customer support phone tree. The first automated agent handles FAQs. If unhandled, it transfers to Tier-1 support. If unresolved, it escalates to Tier-2 engineering.
Interactive Runtime Simulator
Test and tweak inputs dynamicallyInteractive Chain of Responsibility SimulatorLIVE PLAYGROUND
Interact with the controls below to see how this design pattern executes in memory at runtime.
Configure Chain Handlers (Toggle Failures):
CHAIN PIPELINE STATUS
Step 1: AuthHandler.check() -> PASSED
Step 2: RateLimitHandler.check() -> PASSED
Step 3: PayloadGuard.check() -> PASSED
The Problem It Solves
Hardcoding a sequence of checks (authentication, rate limiting, logging, caching) into a monolithic function that is difficult to reorder or extend.
The Architectural Solution
Chain handlers together. Each handler has a reference to `next`. A handler executes its logic and calls `next.handle(request)`.
Pattern Structure & Participants
| Participant | Role & Responsibility | Type |
|---|---|---|
| Handler (Interface / Abstract) | Declares handle() and setNext() | interface |
| AuthHandler, RateLimitHandler | Concrete handlers performing checks | class |
| Client | Assembles chain and passes request | client |
Collaboration Rules:
- Chain: AuthHandler -> RateLimitHandler -> CacheHandler -> Controller
- If any handler fails, it aborts the chain early
Production Code Implementation
Chain of Responsibility.java
public abstract class Middleware {
private Middleware next;
public Middleware linkWith(Middleware next) {
this.next = next;
return next;
}
public abstract boolean check(String user, String role);
protected boolean checkNext(String user, String role) {
if (next == null) return true;
return next.check(user, role);
}
}
// Concrete Handlers
public class RateLimitMiddleware extends Middleware {
public boolean check(String user, String role) {
System.out.println("Checking rate limit for: " + user);
// If rate limit breached, abort chain early!
return checkNext(user, role);
}
}
public class RoleAuthMiddleware extends Middleware {
public boolean check(String user, String role) {
System.out.println("Verifying admin privileges for role: " + role);
if (!"ADMIN".equals(role)) {
System.out.println("403 Forbidden: Insufficient permissions");
return false;
}
return checkNext(user, role);
}
}
// Chain Assembly:
// Middleware chain = new RateLimitMiddleware();
// chain.linkWith(new RoleAuthMiddleware());
// chain.check("john_doe", "USER"); When to Use:
- •When your program is expected to process different kinds of requests in various ways, but the exact types of requests and their sequences are unknown beforehand.
- •When it is essential to execute several handlers in a specific order.
- •When the set of handlers and their order are supposed to change at runtime.
When to Avoid (Anti-Pattern):
- •When requests must always be handled by one specific class and no chaining logic exists.
Staff Engineering Interview Insights & Gotchas
- Every modern web framework's middleware pipeline (Express, Spring, ASP.NET) is an implementation of Chain of Responsibility.
- Show how returning `false` or throwing an exception halts the chain immediately (fail-fast).