The Chain of Responsibility Pattern: Pass It Down
Master the Chain of Responsibility Pattern in Java. Learn how to pass requests along a chain of handlers until one of them handles it.
Moshiour Rahman
The Problem: Rigid Coupling
Imagine you are building a Login system. You need to verify:
- Is the user banned?
- Is the password correct?
- Is the IP allowed?
- Do they have 2FA enabled?
If you put all this in one method, it becomes a mess. If you want to reorder them or make some optional, it’s hard.
The Solution: The Chain of Responsibility
The Chain of Responsibility Pattern lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
Real-Life Analogy: Tech Support 📞
- Level 1 Support: “Have you tried restarting?” (Can’t fix? Pass to L2).
- Level 2 Support: “Is it plugged in?” (Can’t fix? Pass to L3).
- Level 3 Support: “It’s a bug. We’ll fix it.”
The user doesn’t care who fixes it, just that it gets fixed.
Visualizing the Pattern

Implementation
1. The Handler Abstract Class
public abstract class SupportHandler {
private SupportHandler next;
public SupportHandler setNext(SupportHandler next) {
this.next = next;
return next; // Returning next allows chaining: h1.setNext(h2).setNext(h3)
}
public void handleRequest(String issue) {
if (canHandle(issue)) {
resolve(issue);
} else if (next != null) {
next.handleRequest(issue); // Pass it down
} else {
System.out.println("Ticket Unresolved: " + issue);
}
}
protected abstract boolean canHandle(String issue);
protected abstract void resolve(String issue);
}
2. Concrete Handlers
public class Level1Support extends SupportHandler {
@Override
protected boolean canHandle(String issue) {
return issue.equals("basic");
}
@Override
protected void resolve(String issue) {
System.out.println("Level 1 fixed: Turned it off and on again.");
}
}
public class Level2Support extends SupportHandler {
@Override
protected boolean canHandle(String issue) {
return issue.equals("advanced");
}
@Override
protected void resolve(String issue) {
System.out.println("Level 2 fixed: Reinstalled drivers.");
}
}
Usage
SupportHandler l1 = new Level1Support();
SupportHandler l2 = new Level2Support();
// Build the chain
l1.setNext(l2);
// Send requests
l1.handleRequest("basic");
// Output: Level 1 fixed...
l1.handleRequest("advanced");
// Output: Level 2 fixed... (L1 passed it to L2)
l1.handleRequest("impossible");
// Output: Ticket Unresolved
In The Wild (Real World Examples)
1. Servlet Filters / Spring Security Filter Chain
When an HTTP request hits your server, it goes through a chain:
CorsFilter -> AuthFilter -> LoggingFilter -> YourController.
Each filter works and passes it to chain.doFilter().
2. Exception Handling (try-catch)
When an exception is thrown, the JVM looks for a catch block in the current method. If not found, it goes to the caller (Next Handler). This bubbles up the stack until caught.
Cheat Sheet
| Feature | Details |
|---|---|
| Category | Behavioral |
| Problem Solved | Decoupling sender from receiver, sequential processing |
| Key implementation | Linked List of objects (nextHandler) |
| Pros | Dynamic (Change chain order at runtime), Single Responsibility |
| Cons | Performance (Long chains can be slow), Guarantee (Request might fall off the end unhandled) |
Tips to Remember 🧠
- “The Bucket Brigade”: Like passing a bucket of water to put out a fire.
- Middleware: Almost all web middleware (Express.js, Django, Spring) uses this pattern.
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
The Visitor Design Pattern: Add Operations Without Modifying Classes
Master the Visitor Pattern in Java with practical examples. Add new operations to object structures without modifying existing classes using double dispatch.
JavaThe Observer Design Pattern: Don't Call Us, We'll Call You
Master the Observer Pattern in Java. Learn how to implement event-driven architectures and decouple data sources from listeners.
JavaThe Mediator Design Pattern: Centralize Complex Communications
Master the Mediator Pattern in Java. Learn how to reduce coupling by having objects communicate through a mediator instead of directly.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.