Chain of Responsibility Design Pattern
The Chain of Responsibility is a behavioral design pattern. It allows a request to be passed along a chain of handlers. Each handler decides whether it can handle the request or pass it to the next handler in the chain.
In simple words, instead of sending a request directly to a specific object, the request moves through multiple objects until one of them handles it.
Why Do We Need This Pattern?
This pattern is useful when:
- Multiple objects can handle the same request
- You don’t want to tightly couple the sender and receiver
- You want to add or change handlers without changing existing code
It follows the principle of loose coupling and makes the code more flexible and maintainable.
Real-Life Example
Imagine a customer support system:
- Level 1 Support handles basic issues
- Level 2 Support handles technical issues
- Manager handles critical issues
If Level 1 cannot resolve the issue, it forwards the request to Level 2, and so on.
Structure of Chain of Responsibility
- Handler – Defines the interface for handling requests
- Concrete Handlers – Actual classes that handle or forward the request
- Client – Sends the request to the first handler
Java Example: Approval System
Let’s understand this pattern using a simple loan approval example.
Step 1: Create the Handler Interface
public abstract class LoanApprover {
protected LoanApprover nextApprover;
public void setNextApprover(LoanApprover nextApprover) {
this.nextApprover = nextApprover;
}
public abstract void approveLoan(int amount);
}
Step 2: Create Concrete Handlers
public class Manager extends LoanApprover {
@Override
public void approveLoan(int amount) {
if (amount <= 50000) {
System.out.println("Manager approved the loan of " + amount);
} else if (nextApprover != null) {
nextApprover.approveLoan(amount);
}
}
}
public class Director extends LoanApprover {
@Override
public void approveLoan(int amount) {
if (amount <= 200000) {
System.out.println("Director approved the loan of " + amount);
} else if (nextApprover != null) {
nextApprover.approveLoan(amount);
}
}
}
public class CEO extends LoanApprover {
@Override
public void approveLoan(int amount) {
if (amount <= 1000000) {
System.out.println("CEO approved the loan of " + amount);
} else {
System.out.println("Loan amount too high. Rejected.");
}
}
}
Step 3: Client Code
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
LoanApprover manager = new Manager();
LoanApprover director = new Director();
LoanApprover ceo = new CEO();
manager.setNextApprover(director);
director.setNextApprover(ceo);
manager.approveLoan(40000);
manager.approveLoan(150000);
manager.approveLoan(750000);
}
}
Output
Manager approved the loan of 40000
Director approved the loan of 150000
CEO approved the loan of 750000
Key Advantages
- Reduces coupling between sender and receiver
- Easy to add new handlers
- Improves code flexibility
Key Disadvantages
- Request may go unhandled if no handler supports it
- Debugging can be difficult due to multiple handlers
When to Use Chain of Responsibility
- When multiple objects can process a request
- When the exact handler is not known in advance
- When you want to avoid long
if-elsechains
Conclusion
The Chain of Responsibility design pattern helps in creating flexible and loosely coupled systems. By passing requests along a chain, it allows the system to decide the correct handler at runtime, making the application easier to extend and maintain.