Chain of Responsibility Pattern: Elegantly Handle Multi-Level Requests in Java
This article explains the Chain of Responsibility design pattern with a practical Java example of a leave approval workflow, showing how it decouples request senders from receivers and enables flexible, extensible handling through a chain of handlers.
Introduction
The article opens with a real-world scenario: looking up an IP location by trying multiple external services (Taobao, Baidu, etc.) sequentially until one succeeds. This motivates the need for a pattern that handles requests through a chain of processors, improving extensibility and maintainability.
Pattern Definition
The Chain of Responsibility pattern allows multiple objects to handle a request, avoiding coupling between sender and receiver. Receivers are linked in a chain; the request passes along until an object handles it.
Core Roles
Abstract Handler (Handler) : Defines the common request handling interface and a reference to the next handler.
Concrete Handler (Concrete Handler) : Implements the handling logic; if it cannot handle the request, it forwards to the next handler.
Client (Client) : Builds the chain and initiates the request without knowing the concrete handlers or chain length.
UML Class Diagram
Java Implementation: Leave Approval Example
The article demonstrates the pattern with a company leave approval workflow:
≤3 days: Team Leader approves.
3–5 days: Manager approves.
>5 days: Director approves.
Request Class
package com.sample.patterns.cor;
// Request class
public class Request {
// Leave days
private Integer days;
private Integer requestId;
public Request(Integer days, Integer requestId) {
this.days = days;
this.requestId = requestId;
}
public Integer getDays() {
return days;
}
public void setDays(Integer days) {
this.days = days;
}
public Integer getRequestId() {
return requestId;
}
public void setRequestId(Integer requestId) {
this.requestId = requestId;
}
}Abstract Handler
package com.sample.patterns.cor;
// Abstract Handler
public abstract class Handler {
// Next handler
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public Handler getSuccessor() {
return this.successor;
}
public abstract void handleRequest(Request request);
}Concrete Handlers
LeaderHandler (Team Leader): handles requests ≤3 days.
package com.sample.patterns.cor;
// Team Leader handler
public class LeaderHandler extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getDays() <= 3) {
System.out.println("Team Leader approves!");
} else {
getSuccessor().handleRequest(request);
}
}
}ManagerHandler (Manager): handles requests >3 and ≤5 days.
package com.sample.patterns.cor;
// Manager handler
public class ManagerHandler extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getDays() > 3 && request.getDays() <= 5) {
System.out.println("Manager approves!");
} else {
getSuccessor().handleRequest(request);
}
}
}DirectorHandler (Director): handles requests >5 days.
package com.sample.patterns.cor;
// Director handler
public class DirectorHandler extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getDays() > 5) {
System.out.println("Director approves!");
} else {
getSuccessor().handleRequest(request);
}
}
}Client: Building and Testing the Chain
package com.sample.patterns.cor;
public class Client {
public static void main(String[] args) {
Handler leader = new LeaderHandler();
Handler manager = new ManagerHandler();
Handler director = new DirectorHandler();
leader.setSuccessor(manager);
manager.setSuccessor(director);
Request request1 = new Request(4, 1);
leader.handleRequest(request1);
Request request2 = new Request(10, 2);
leader.handleRequest(request2);
}
}Execution Results and Extensibility
Testing shows:
4-day request: LeaderHandler cannot handle (exceeds 3 days), passes to ManagerHandler which approves.
10-day request: Passes through LeaderHandler and ManagerHandler, finally approved by DirectorHandler.
Adding a new approval level (e.g., General Manager) only requires creating a new GeneralManagerHandler and linking it after DirectorHandler, without modifying existing handler code (though DirectorHandler's threshold may need adjustment). This demonstrates the pattern's decoupling and extensibility.
Conclusion
The Chain of Responsibility pattern decouples request senders from receivers, enhancing code flexibility and extensibility while keeping the structure elegant.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
