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
Advertisement
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.
Advertisement
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. Learn how to add new operations to object structures 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.