Chain of Responsibility Design Pattern in Java
1. Introduction
In large systems, requests often need to be processed by multiple handlers. Instead of tightly coupling the request sender to a specific handler, the Chain of Responsibility (CoR) pattern allows a request to pass through a chain of handlers until one of them handles it.
This pattern decouples the sender from the receiver, making the system flexible and easier to extend.
2. Real-Life Analogy
Imagine a customer support system:
- If the query is simple, the Support Executive handles it.
- If it’s more complex, it goes to the Team Lead.
- If still unresolved, it escalates to the Manager.
Each level decides whether it can handle the request or pass it to the next handler.
3. Structure
- Handler (abstract class or interface) → Defines a method for handling requests and maintains a reference to the next handler.
- Concrete Handlers → Implement request handling. If unable, forward to the next handler.
- Client → Initiates the request, unaware of which handler will process it.
4. Custom Java Implementation
Step 1: Create the Handler Interface
abstract class SupportHandler {
protected SupportHandler nextHandler;
// Set next handler in chain
public void setNextHandler(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
// Abstract method to handle request
public abstract void handleRequest(String issueLevel);
}
Step 2: Concrete Handlers
class SupportExecutive extends SupportHandler {
@Override
public void handleRequest(String issueLevel) {
if (issueLevel.equalsIgnoreCase("Basic")) {
System.out.println("Support Executive resolved the issue.");
} else if (nextHandler != null) {
System.out.println("Support Executive escalates the issue...");
nextHandler.handleRequest(issueLevel);
}
}
}
class TeamLead extends SupportHandler {
@Override
public void handleRequest(String issueLevel) {
if (issueLevel.equalsIgnoreCase("Intermediate")) {
System.out.println("Team Lead resolved the issue.");
} else if (nextHandler != null) {
System.out.println("Team Lead escalates the issue...");
nextHandler.handleRequest(issueLevel);
}
}
}
class Manager extends SupportHandler {
@Override
public void handleRequest(String issueLevel) {
if (issueLevel.equalsIgnoreCase("Critical")) {
System.out.println("Manager resolved the issue.");
} else if (nextHandler != null) {
System.out.println("Manager escalates the issue further...");
nextHandler.handleRequest(issueLevel);
} else {
System.out.println("Issue could not be resolved.");
}
}
}
Step 3: Client Code
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// Create handlers
SupportHandler executive = new SupportExecutive();
SupportHandler teamLead = new TeamLead();
SupportHandler manager = new Manager();
// Set the chain
executive.setNextHandler(teamLead);
teamLead.setNextHandler(manager);
// Client sends different types of issues
System.out.println("Case 1: Basic Issue");
executive.handleRequest("Basic");
System.out.println("\nCase 2: Intermediate Issue");
executive.handleRequest("Intermediate");
System.out.println("\nCase 3: Critical Issue");
executive.handleRequest("Critical");
System.out.println("\nCase 4: Unknown Issue");
executive.handleRequest("Unknown");
}
}
5. Output
Case 1: Basic Issue
Support Executive resolved the issue.
Case 2: Intermediate Issue
Support Executive escalates the issue...
Team Lead resolved the issue.
Case 3: Critical Issue
Support Executive escalates the issue...
Team Lead escalates the issue...
Manager resolved the issue.
Case 4: Unknown Issue
Support Executive escalates the issue...
Team Lead escalates the issue...
Manager escalates the issue further...
Issue could not be resolved.
6. Benefits
- Decouples sender and receiver – the client doesn’t care who handles the request.
- Flexible and extensible – new handlers can be added without changing existing code.
- Clear separation of responsibilities.
7. When to Use
- When multiple objects can handle a request.
- When the handler is not known in advance.
- When you want to give more than one object a chance to process a request.