Java設計模式之職責鏈模式詳解
一、職責鏈模式核心思想
核心目標:將請求的發送者與接收者解耦,使多個對象都有機會處理請求。這些處理者形成鏈式結構,請求沿鏈傳遞直到被處理或到達鏈尾,如政府審批層層上報機制。
二、職責鏈模式類圖(Mermaid)
三、代碼實現示例
1. 請假審批場景
// 抽象處理者
abstract class Approver {protected Approver nextApprover;protected String name;protected int maxApproveDays;public Approver(String name, int maxDays) {this.name = name;this.maxApproveDays = maxDays;}public Approver setNext(Approver next) {this.nextApprover = next;return this;}public void processRequest(LeaveRequest request) {if (request.getDays() <= maxApproveDays) {System.out.printf("%s 審批了 %d 天請假(原因:%s)\n", name, request.getDays(), request.getReason());} else if (nextApprover != null) {nextApprover.processRequest(request);} else {System.out.println("請假天數過長,無人可審批!");}}
}// 具體處理者
class Manager extends Approver {public Manager() { super("張經理", 3); }
}class Director extends Approver {public Director() { super("王總監", 7); }
}class CEO extends Approver {public CEO() { super("李總裁", 15); }
}// 請求對象
class LeaveRequest {private int days;private String reason;// 構造方法、getter省略
}// 客戶端調用
Approver chain = new Manager().setNext(new Director()).setNext(new CEO());chain.processRequest(new LeaveRequest(5, "看病"));
// 輸出:王總監 審批了 5 天請假(原因:看病)
chain.processRequest(new LeaveRequest(20, "旅游"));
// 輸出:請假天數過長,無人可審批!
四、模式優缺點分析
? 優勢
- 解耦請求與處理:發送者無需知道具體處理者
- 動態調整責任鏈:可運行時修改鏈結構
- 符合開閉原則:新增處理者無需修改已有代碼
? 缺點
- 請求可能未被處理:需要設計默認處理邏輯
- 性能影響:長鏈可能導致請求處理延遲
- 調試困難:請求傳遞路徑不易追蹤
五、典型應用場景
- 多級審批系統:OA系統中的請假/報銷審批
- 異常處理機制:Spring MVC的異常處理鏈
- 過濾器/攔截器鏈:Servlet Filter、Spring Interceptor
- 日志處理系統:不同級別日志(DEBUG/INFO/ERROR)分發
- 游戲事件處理:UI元素的事件冒泡機制
六、Mermaid序列圖(請求傳遞流程)
七、職責鏈模式 vs 其他模式
對比模式 | 核心區別 |
---|---|
裝飾器模式 | 層層增強功能,所有處理者必須執行 |
命令模式 | 封裝請求為對象,關注請求生命周期 |
狀態模式 | 狀態轉換改變行為,處理者內部切換 |
八、高級應用技巧
1. 組合模式實現樹形責任鏈
2. 中斷鏈式傳遞
public void processRequest(Request req) {if (canHandle(req)) {// 處理邏輯return; // 中斷傳遞}if (next != null) next.processRequest(req);
}
九、Spring框架應用案例
Spring Security過濾器鏈
十、常見問題解答
Q1:如何避免循環鏈?
- 設置最大傳遞深度:計數器限制鏈長度
- 使用DAG驗證:構建鏈時檢查環狀結構
- 單元測試覆蓋:驗證鏈式調用路徑
Q2:如何處理異步責任鏈?
- 使用CompletableFuture:Java 8+的異步編程
public CompletableFuture<Void> asyncProcess(Request req) {return CompletableFuture.runAsync(() -> process(req));
}
Q3:如何調試責任鏈?
- 添加Trace ID:為請求添加唯一標識
- 日志記錄器:每個處理者記錄處理狀態
public void process(Request req) {log.debug("Handler {} 開始處理請求 {}", name, req.getId());// 處理邏輯
}