The Chain of Responsibility pattern is a behavioural design pattern that allows passing a request along a chain of potential handlers until one of them handles it. Each handler in the chain either processes the request or passes it to the next handler.
This pattern decouples the sender of the request from its receiver, giving multiple objects the chance to handle the request dynamically.
Applicability
- When more than one object may handle a request, and the handler isn’t known in advance.
- When you want to issue a request to one of several objects without tightly coupling the sender to the receiver.
- When processing steps should be dynamically composed or reordered.
Pros and Cons
Advantages
- Decouples sender and receiver
- The sender doesn’t need to know which object will handle the request.
- Promotes flexibility
- Handlers can be changed or added dynamically without modifying the request sender.
- Follows Single Responsibility Principle
- Each handler deals with a specific kind of request, keeping responsibilities clear and focused.
Drawbacks
- No guarantee that a request will be handled
- If no handler handles the request, it might be dropped silently unless handled explicitly.
- Debugging is harder
- It can be tricky to trace the request flow, especially when chains are long or dynamically built.
- Reduced performance
- Especially if the chain is long and each handler does non-trivial checks.
Approach
- Define a Handler interface that declares a method for handling requests and for setting the next handler.
- Implement ConcreteHandlers that either handle the request or pass it to the next handler in the chain.
- The Client builds the chain and submits requests to the first handler.
Components
- Handler (interface or abstract class)
- Declares a method for handling requests and optionally storing a reference to the next handler.
- ConcreteHandler
- Handles the request it is responsible for, or passes it along the chain.
classDiagram class Handler { <<abstract>> -successor: Handler +__init__(successor: Handler) +handle_request(request) +can_handle(request)* abstract } class ConcreteHandlerA { +can_handle(request) +handle_request(request) } class ConcreteHandlerB { +can_handle(request) +handle_request(request) } Handler <|-- ConcreteHandlerA : implements Handler <|-- ConcreteHandlerB : implements Handler --> Handler : successor
Example
Let’s consider a basic Support Request example, where a request is handled by Level 1, Level 2, or Manager support.
Handler
interface SupportHandler {
void setNext(SupportHandler next);
void handleRequest(String level);
}
Concrete handler
class Level1Support implements SupportHandler {
private SupportHandler next;
@Override
public void setNext(SupportHandler next) {
this.next = next;
}
@Override
public void handleRequest(String level) {
if (level.equals("level1")) {
System.out.println("Level 1 support handled the request.");
} else if (next != null) {
next.handleRequest(level);
} else {
System.out.println("No handler available for: " + level);
}
}
}
class Level2Support implements SupportHandler {
private SupportHandler next;
@Override
public void setNext(SupportHandler next) {
this.next = next;
}
@Override
public void handleRequest(String level) {
if (level.equals("level2")) {
System.out.println("Level 2 support handled the request.");
} else if (next != null) {
next.handleRequest(level);
} else {
System.out.println("No handler available for: " + level);
}
}
}
class ManagerSupport implements SupportHandler {
private SupportHandler next;
@Override
public void setNext(SupportHandler next) {
this.next = next;
}
@Override
public void handleRequest(String level) {
if (level.equals("manager")) {
System.out.println("Manager handled the request.");
} else if (next != null) {
next.handleRequest(level);
} else {
System.out.println("No handler available for: " + level);
}
}
}
Client
public class ChainOfResponsibilityExample {
public static void main(String[] args) {
// Create handlers
SupportHandler level1 = new Level1Support();
SupportHandler level2 = new Level2Support();
SupportHandler manager = new ManagerSupport();
// Set up chain
level1.setNext(level2);
level2.setNext(manager);
// Client sends requests
level1.handleRequest("level1");
level1.handleRequest("level2");
level1.handleRequest("manager");
level1.handleRequest("unknown");
}
}
Level 1 support handled the request.
Level 2 support handled the request.
Manager handled the request.
No handler available for: unknown
Back to parent page: Behavioural Patterns
Design_Pattern Behavioural_Design_Patterns Chain_Of_Responsibility SOFT2201