Understanding Chain of Responsibility Design Pattern in Java
The Chain of Responsibility Design Pattern is a popular behavioral design pattern in Java used to process a request through a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain. This approach reduces tight coupling, improves flexibility, and makes the request-processing pipeline highly maintainable.
If you build applications involving request validation, filtering, logging, authentication, authorization, exception handling, or event processing, you will find this pattern extremely valuable.
What is the Chain of Responsibility Pattern?
The Chain of Responsibility Pattern allows multiple objects to handle a request without the sender knowing which object will process it. The request flows through a chain of handlers until one of them handles it.
Real-Time Use Cases
- Servlet Filters in Java Web Applications
- Spring Security Authentication Chain
- ATM withdrawal processing (different responsibilities)
- Logging frameworks like Log4j, SLF4J
- Customer request escalation (support level 1 → level 2 → level 3)
Chain of Responsibility Pattern – Real-Time Java Example (Support Ticket Handling)
Consider a customer support system where support tickets escalate automatically:
Level 1 → Level 2 → Level 3
If Level 1 can’t handle the request, it should pass it forward.
Step 1: Base Handler
public abstract class SupportHandler {
protected SupportHandler nextHandler;
public void setNextHandler(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(String issueType);
}
Step 2: Concrete Handlers
public class Level1Support extends SupportHandler {
@Override
public void handleRequest(String issueType) {
if (issueType.equals("Basic")) {
System.out.println("Level 1 Support: Issue resolved.");
} else if (nextHandler != null) {
nextHandler.handleRequest(issueType);
}
}
}
public class Level2Support extends SupportHandler {
@Override
public void handleRequest(String issueType) {
if (issueType.equals("Advanced")) {
System.out.println("Level 2 Support: Issue resolved.");
} else if (nextHandler != null) {
nextHandler.handleRequest(issueType);
}
}
}
public class Level3Support extends SupportHandler {
@Override
public void handleRequest(String issueType) {
if (issueType.equals("Critical")) {
System.out.println("Level 3 Support: Critical issue resolved.");
} else {
System.out.println("Issue cannot be handled.");
}
}
}
Step 3: Client Code – Building the Chain
public class SupportDemo {
public static void main(String[] args) {
SupportHandler level1 = new Level1Support();
SupportHandler level2 = new Level2Support();
SupportHandler level3 = new Level3Support();
level1.setNextHandler(level2);
level2.setNextHandler(level3);
level1.handleRequest("Basic");
level1.handleRequest("Advanced");
level1.handleRequest("Critical");
}
}
Output
Level 1 Support: Issue resolved. Level 2 Support: Issue resolved. Level 3 Support: Critical issue resolved.
Why Use Chain of Responsibility?
- Decouples sender and receiver
- Easy to add new handlers without changing existing code
- Improves flexibility and maintainability
- Enables dynamic ordering of request handlers
When Should You Use It?
- When multiple objects can handle a request
- When the handler should be selected at runtime
- When you want to avoid huge if-else or switch statements
Advantages
- Flexible chain configuration
- Cleaner code compared to conditional logic
- Follows Open/Closed Principle
Disadvantages
- No guarantee a request will be handled
- Debugging can become harder with long chains
Conclusion
The Chain of Responsibility Pattern in Java is perfect when you want to build clean, maintainable pipelines for request processing. From logging and filtering to validation and authentication, this pattern helps you avoid complex conditional logic and improve your system architecture.
Comments
Post a Comment