設計模式(十四)行為型:職責鏈模式詳解
職責鏈模式(Chain of Responsibility Pattern)是 GoF 23 種設計模式中的行為型模式之一,其核心價值在于將多個處理對象(處理器)連接成一條鏈,使請求沿著鏈傳遞,直到被某個處理器處理為止。它解耦了請求的發送者與接收者,允許動態地組織處理流程,提升系統的靈活性與可擴展性。職責鏈模式是實現“開閉原則”的典范,廣泛應用于審批流程(請假、報銷)、事件處理(GUI 事件分發)、日志系統(多級日志處理器)、中間件管道(如 Web 框架的過濾器鏈)、異常處理、權限校驗等需要多級判斷或順序處理的場景,是構建可配置、可插拔業務流程的關鍵架構模式。
一、詳細介紹
職責鏈模式解決的是“一個請求可能由多個對象處理,但具體由誰處理在運行時決定”的問題。在傳統設計中,客戶端需要顯式判斷由哪個對象處理請求,導致代碼中充斥條件判斷(if-else 或 switch),難以維護和擴展。當處理邏輯變更或新增處理器時,客戶端代碼必須修改。
職責鏈模式通過將多個處理器組織成一條鏈,客戶端只需將請求發送給鏈的首節點,后續傳遞由處理器自行決定。每個處理器都持有對下一個處理器的引用(或通過外部容器管理),并在處理請求時:
- 判斷自己是否能處理該請求;
- 若能處理,則執行業務邏輯并結束;
- 若不能處理,則將請求轉發給鏈中的下一個處理器;
- 若鏈尾仍未處理,則可選擇丟棄或拋出異常。
該模式包含以下核心角色:
- Handler(抽象處理器):定義處理請求的接口,通常包含一個方法(如
handleRequest()
)和一個指向后繼處理器的引用(successor
)。可以是抽象類或接口。 - ConcreteHandler(具體處理器):實現
Handler
接口,包含具體的處理邏輯。它決定是否處理當前請求,若不處理則將請求轉發給后繼。 - Client(客戶端):創建處理器鏈,并向鏈的首節點發送請求。客戶端不關心具體由哪個處理器處理,也不依賴具體處理器類型。
職責鏈的組織方式有兩種:
- 顯式鏈(Explicit Chain):每個處理器持有對下一個處理器的引用,形成鏈式結構。
- 中心化鏈(Centralized Chain):由一個容器(如
Chain
類)管理處理器列表,按順序調用。
職責鏈模式的關鍵優勢:
- 解耦請求發送者與接收者:客戶端無需知道具體處理者。
- 增強系統靈活性:可動態添加、刪除或重排處理器。
- 符合開閉原則:新增處理器無需修改現有代碼。
- 支持多種處理邏輯:可實現“首個匹配即處理”或“全部處理”等策略。
與“狀態模式”相比,職責鏈關注請求的傳遞與處理,狀態模式關注對象行為隨狀態改變;與“觀察者模式”相比,職責鏈是單向鏈式傳遞,觀察者是廣播式通知;與“策略模式”相比,職責鏈允許多個策略順序參與,策略模式是單一策略選擇。
二、職責鏈模式的UML表示
以下是職責鏈模式的標準 UML 類圖:
圖解說明:
Handler
定義處理接口和后繼引用。ConcreteHandlerA/B/C
實現具體處理邏輯,可選擇處理請求或轉發。- 處理器通過
setSuccessor()
構成鏈。 - 客戶端向鏈首發送請求,請求沿鏈傳遞。
三、一個簡單的Java程序實例及其UML圖
以下是一個公司請假審批系統的示例,展示不同級別的管理者對不同天數的請假請求進行審批。
Java 程序實例
// 請求類
class LeaveRequest {private String employee;private int days;private String reason;public LeaveRequest(String employee, int days, String reason) {this.employee = employee;this.days = days;this.reason = reason;}// Getter 方法public String getEmployee() { return employee; }public int getDays() { return days; }public String getReason() { return reason; }@Overridepublic String toString() {return "LeaveRequest{" +"employee='" + employee + '\'' +", days=" + days +", reason='" + reason + '\'' +'}';}
}// 抽象處理器:審批者
abstract class Approver {protected Approver successor;protected String name;protected String position;public Approver(String name, String position) {this.name = name;this.position = position;}public void setSuccessor(Approver successor) {this.successor = successor;}// 處理請求的抽象方法public abstract void handleRequest(LeaveRequest request);
}// 具體處理器:組長(可批1-3天)
class TeamLeader extends Approver {public TeamLeader(String name) {super(name, "Team Leader");}@Overridepublic void handleRequest(LeaveRequest request) {if (request.getDays() <= 3) {System.out.println("? [" + position + " " + name + "] 批準了 " + request.getEmployee() +" 的 " + request.getDays() + " 天請假申請。");} else if (successor != null) {System.out.println("?? [" + position + " " + name + "] 無法處理,轉交上級...");successor.handleRequest(request);}}
}// 具體處理器:部門經理(可批4-7天)
class DepartmentManager extends Approver {public DepartmentManager(String name) {super(name, "Department Manager");}@Overridepublic void handleRequest(LeaveRequest request) {if (request.getDays() <= 7) {System.out.println("? [" + position + " " + name + "] 批準了 " + request.getEmployee() +" 的 " + request.getDays() + " 天請假申請。");} else if (successor != null) {System.out.println("?? [" + position + " " + name + "] 無法處理,轉交上級...");successor.handleRequest(request);}}
}// 具體處理器:總經理(可批任意天數)
class GeneralManager extends Approver {public GeneralManager(String name) {super(name, "General Manager");}@Overridepublic void handleRequest(LeaveRequest request) {System.out.println("? [" + position + " " + name + "] 特批了 " + request.getEmployee() +" 的 " + request.getDays() + " 天請假申請。");}
}// 客戶端使用示例
public class ChainOfResponsibilityDemo {public static void main(String[] args) {System.out.println("🏢 公司請假審批系統 - 職責鏈模式示例\n");// 創建處理器鏈Approver teamLeader = new TeamLeader("張組長");Approver deptManager = new DepartmentManager("李經理");Approver gm = new GeneralManager("王總");// 構建鏈:組長 -> 部門經理 -> 總經理teamLeader.setSuccessor(deptManager);deptManager.setSuccessor(gm);// 模擬不同請假請求LeaveRequest req1 = new LeaveRequest("小明", 2, "感冒");LeaveRequest req2 = new LeaveRequest("小紅", 5, "家庭事務");LeaveRequest req3 = new LeaveRequest("小剛", 10, "出國旅游");System.out.println("📝 處理請假請求:");teamLeader.handleRequest(req1);System.out.println("---");teamLeader.handleRequest(req2);System.out.println("---");teamLeader.handleRequest(req3);System.out.println("\n💡 說明:請求沿鏈傳遞,直到被合適處理器處理。");System.out.println("🔧 可動態調整鏈結構,如新增總監層。");}
}
實例對應的UML圖(簡化版)
運行說明:
Approver
定義了處理鏈的結構和接口。TeamLeader
、DepartmentManager
、GeneralManager
實現具體審批邏輯。- 請求從
teamLeader
開始,根據天數逐級傳遞。 - 客戶端只需將請求交給鏈首,無需關心處理細節。
四、總結
特性 | 說明 |
---|---|
核心目的 | 解耦請求發送者與接收者,實現請求的動態處理 |
實現機制 | 構建處理器鏈,請求沿鏈傳遞直至被處理 |
優點 | 降低耦合、增強靈活性、支持動態配置、符合開閉原則 |
缺點 | 請求可能未被處理(需設計默認處理)、性能可能下降(鏈過長)、調試困難 |
適用場景 | 審批流程、事件處理、日志分級、過濾器鏈、異常處理、權限校驗 |
不適用場景 | 處理邏輯簡單、必須由特定對象處理、性能敏感且鏈過長 |
職責鏈模式使用建議:
- 應確保鏈中有至少一個處理器能處理請求,避免“黑洞”。
- 可引入“默認處理器”處理未匹配請求。
- 支持運行時動態構建和修改鏈結構。
- 在 Java 中,Servlet 的
FilterChain
、Spring 的攔截器鏈是典型應用。
架構師洞見:
職責鏈模式是“流程可配置化”與“關注點分離”的高級體現。在現代架構中,其思想已演變為中間件管道、事件驅動架構和工作流引擎的核心。例如,在 Web 框架(如 Express、Koa)中,中間件鏈按順序處理 HTTP 請求;在微服務中,API 網關的過濾器鏈執行認證、限流、日志等操作;在工作流引擎(如 Activiti)中,任務節點構成處理鏈;在前端框架中,事件冒泡機制本質是職責鏈。未來趨勢是:職責鏈將與低代碼流程引擎深度融合,通過可視化拖拽構建處理鏈;在AI Agent 系統中,Agent 的“決策鏈”(Reasoning Chain)可視為職責鏈,每個步驟由不同工具或模型處理;在可觀測性系統中,日志、指標、追蹤數據將通過職責鏈進行分級處理與路由。
掌握職責鏈模式,有助于設計出靈活、可配置、易擴展的業務流程。作為架構師,應在涉及“多級判斷”、“順序處理”或“流程編排”的場景中主動引入職責鏈。職責鏈不僅是模式,更是流程治理的哲學——它提醒我們:真正的靈活性,來自于將“決策路徑”從硬編碼中解放出來,交由配置與鏈式邏輯動態決定。