設計模式(十四)行為型:職責鏈模式詳解

設計模式(十四)行為型:職責鏈模式詳解

職責鏈模式(Chain of Responsibility Pattern)是 GoF 23 種設計模式中的行為型模式之一,其核心價值在于將多個處理對象(處理器)連接成一條鏈,使請求沿著鏈傳遞,直到被某個處理器處理為止。它解耦了請求的發送者與接收者,允許動態地組織處理流程,提升系統的靈活性與可擴展性。職責鏈模式是實現“開閉原則”的典范,廣泛應用于審批流程(請假、報銷)、事件處理(GUI 事件分發)、日志系統(多級日志處理器)、中間件管道(如 Web 框架的過濾器鏈)、異常處理、權限校驗等需要多級判斷或順序處理的場景,是構建可配置、可插拔業務流程的關鍵架構模式。

一、詳細介紹

職責鏈模式解決的是“一個請求可能由多個對象處理,但具體由誰處理在運行時決定”的問題。在傳統設計中,客戶端需要顯式判斷由哪個對象處理請求,導致代碼中充斥條件判斷(if-else 或 switch),難以維護和擴展。當處理邏輯變更或新增處理器時,客戶端代碼必須修改。

職責鏈模式通過將多個處理器組織成一條鏈,客戶端只需將請求發送給鏈的首節點,后續傳遞由處理器自行決定。每個處理器都持有對下一個處理器的引用(或通過外部容器管理),并在處理請求時:

  1. 判斷自己是否能處理該請求;
  2. 若能處理,則執行業務邏輯并結束;
  3. 若不能處理,則將請求轉發給鏈中的下一個處理器;
  4. 若鏈尾仍未處理,則可選擇丟棄或拋出異常。

該模式包含以下核心角色:

  • Handler(抽象處理器):定義處理請求的接口,通常包含一個方法(如 handleRequest())和一個指向后繼處理器的引用(successor)。可以是抽象類或接口。
  • ConcreteHandler(具體處理器):實現 Handler 接口,包含具體的處理邏輯。它決定是否處理當前請求,若不處理則將請求轉發給后繼。
  • Client(客戶端):創建處理器鏈,并向鏈的首節點發送請求。客戶端不關心具體由哪個處理器處理,也不依賴具體處理器類型。

職責鏈的組織方式有兩種:

  1. 顯式鏈(Explicit Chain):每個處理器持有對下一個處理器的引用,形成鏈式結構。
  2. 中心化鏈(Centralized Chain):由一個容器(如 Chain 類)管理處理器列表,按順序調用。

職責鏈模式的關鍵優勢:

  • 解耦請求發送者與接收者:客戶端無需知道具體處理者。
  • 增強系統靈活性:可動態添加、刪除或重排處理器。
  • 符合開閉原則:新增處理器無需修改現有代碼。
  • 支持多種處理邏輯:可實現“首個匹配即處理”或“全部處理”等策略。

與“狀態模式”相比,職責鏈關注請求的傳遞與處理,狀態模式關注對象行為隨狀態改變;與“觀察者模式”相比,職責鏈是單向鏈式傳遞,觀察者是廣播式通知;與“策略模式”相比,職責鏈允許多個策略順序參與,策略模式是單一策略選擇

二、職責鏈模式的UML表示

以下是職責鏈模式的標準 UML 類圖:

implements
implements
implements
successor
sends request
?abstract?
Handler
-successor: Handler
+setSuccessor(successor: Handler)
+handleRequest(request: Request)
ConcreteHandlerA
+handleRequest(request: Request)
ConcreteHandlerB
+handleRequest(request: Request)
ConcreteHandlerC
+handleRequest(request: Request)
Client
+main(args: String[])

圖解說明

  • 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圖(簡化版)
extends
extends
extends
successor
sends request
creates
LeaveRequest
-employee: String
-days: int
-reason: String
+getEmployee()
+getDays()
+getReason()
?abstract?
Approver
-successor: Approver
-name: String
-position: String
+setSuccessor(successor: Approver)
+handleRequest(request: LeaveRequest)
TeamLeader
+handleRequest(request: LeaveRequest)
DepartmentManager
+handleRequest(request: LeaveRequest)
GeneralManager
+handleRequest(request: LeaveRequest)
Client
+main(args: String[])

運行說明

  • Approver 定義了處理鏈的結構和接口。
  • TeamLeaderDepartmentManagerGeneralManager 實現具體審批邏輯。
  • 請求從 teamLeader 開始,根據天數逐級傳遞。
  • 客戶端只需將請求交給鏈首,無需關心處理細節。

四、總結

特性說明
核心目的解耦請求發送者與接收者,實現請求的動態處理
實現機制構建處理器鏈,請求沿鏈傳遞直至被處理
優點降低耦合、增強靈活性、支持動態配置、符合開閉原則
缺點請求可能未被處理(需設計默認處理)、性能可能下降(鏈過長)、調試困難
適用場景審批流程、事件處理、日志分級、過濾器鏈、異常處理、權限校驗
不適用場景處理邏輯簡單、必須由特定對象處理、性能敏感且鏈過長

職責鏈模式使用建議

  • 應確保鏈中有至少一個處理器能處理請求,避免“黑洞”。
  • 可引入“默認處理器”處理未匹配請求。
  • 支持運行時動態構建和修改鏈結構。
  • 在 Java 中,Servlet 的 FilterChain、Spring 的攔截器鏈是典型應用。

架構師洞見:
職責鏈模式是“流程可配置化”與“關注點分離”的高級體現。在現代架構中,其思想已演變為中間件管道事件驅動架構工作流引擎的核心。例如,在 Web 框架(如 Express、Koa)中,中間件鏈按順序處理 HTTP 請求;在微服務中,API 網關的過濾器鏈執行認證、限流、日志等操作;在工作流引擎(如 Activiti)中,任務節點構成處理鏈;在前端框架中,事件冒泡機制本質是職責鏈。

未來趨勢是:職責鏈將與低代碼流程引擎深度融合,通過可視化拖拽構建處理鏈;在AI Agent 系統中,Agent 的“決策鏈”(Reasoning Chain)可視為職責鏈,每個步驟由不同工具或模型處理;在可觀測性系統中,日志、指標、追蹤數據將通過職責鏈進行分級處理與路由。

掌握職責鏈模式,有助于設計出靈活、可配置、易擴展的業務流程。作為架構師,應在涉及“多級判斷”、“順序處理”或“流程編排”的場景中主動引入職責鏈。職責鏈不僅是模式,更是流程治理的哲學——它提醒我們:真正的靈活性,來自于將“決策路徑”從硬編碼中解放出來,交由配置與鏈式邏輯動態決定。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916498.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WAIC 2025 熱點解讀:如何構建 AI 時代的“視頻神經中樞”?

一、&#x1f310; WAIC 2025 大會看點&#xff1a;AI 正在“長出眼睛與身體” 在 2025 年的人工智能大會&#xff08;WAIC 2025&#xff09;上&#xff0c;“大模型退幕后&#xff0c;具身智能登場”成為最具共識的趨勢轉向。從展區到主論壇&#xff0c;再到各大企業發布的新…

OpenCV+Python

安裝 OpenCV&#xff1a; Python&#xff1a;直接 pip install opencv-python&#xff08;核心庫&#xff09;和 opencv-contrib-python&#xff08;擴展功能&#xff09;。 pip install opencv-python pip install opencv-contrib-python 驗證安裝&#xff1a; import cv2…

現代C++的一般編程規范

一般情況下不要使用std::endl&#xff0c;尤其是在循環中&#xff0c;因為可能一開始你只是想要打印一個換行符&#xff0c;但是"endl"做的更多&#xff0c;其還會刷新緩沖區&#xff0c;這會額外花費很多時間&#xff0c;相反&#xff0c;只需要使用“\n"&…

38.安卓逆向2-frida hook技術-過firda檢測(三)(通過SO文件過檢測原理)

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a;圖靈Python學院 工具下載&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取碼&#xff1…

創建屬于自己的github Page主頁

安裝手冊 安裝手冊 環境要求 Node.js version 18.0 安裝 Node.js 時&#xff0c;建議勾選所有和依賴相關的選項。 安裝步驟 安裝 Docusaurus 最簡單的方法是使用 create-docusaurus 命令行工具&#xff0c;它可以幫助你快速搭建一個 Docusaurus 網站的基礎框架。 你可以在…

Unity Catalog與Apache Iceberg如何重塑Data+AI時代的企業數據架構

在2025年DataAI Summit上&#xff0c;Databricks發布了一系列重大更新&#xff0c;標志著企業數據治理進入新階段。其中&#xff0c;Unity Catalog的增強功能和對Apache Iceberg的全面支持尤為引人注目。這些更新不僅強化了跨平臺數據管理能力&#xff0c;還推動了開放數據生態…

雨季,汽車經常跑山區,該如何保養?

雨季來臨&#xff0c;山區道路變得濕滑難行&#xff0c;頻繁穿梭于此的汽車面臨著前所未有的挑戰。如何在這樣惡劣的環境中確保愛車安然無恙&#xff1f;本文將為你詳細解析雨季經常跑山區的汽車該如何保養&#xff0c;讓你在遭遇突發狀況時也能從容應對。當雨季遇上山區路況&a…

Spring Boot音樂服務器項目-查詢音樂模塊

一、項目架構概覽 該音樂播放服務器采用經典的MVC分層架構&#xff0c;核心模塊包括&#xff1a; 實體層&#xff1a;定義數據模型Mapper層&#xff1a;數據庫操作接口Controller層&#xff1a;HTTP請求處理工具層&#xff1a;加密、響應封裝等輔助功能 項目核心功能包括用戶…

Imagine:高效免費的圖片壓縮工具

很多時候&#xff0c;我們需要對圖片進行壓縮&#xff0c;卻苦于找不到免費又好用的工具。這里給大家推薦一款電腦端的圖片壓縮軟件——Imagine。 Imagine文末獲取 它有諸多優點&#xff1a; 開源免費&#xff1a;無需擔心付費問題&#xff0c;完全免費使用。 便捷易用&#…

《Uniapp-Vue 3-TS 實戰開發》自定義年月日時分秒picker組件

目前組件: 組件完整代碼: <template><view><picker mode="multiSelector" :value="multiIndex" :range="multiRange" @change="onMultiChange"><view class="picker">{{ formattedDateTime }}&l…

生命通道的智慧向導:Deepoc具身智能如何重塑醫院導診機器人的“仁心慧眼”

生命通道的智慧向導&#xff1a;Deepoc具身智能如何重塑醫院導診機器人的“仁心慧眼”清晨八點的三甲醫院門診大廳&#xff0c;一臺導診機器人突然轉向無障礙通道。視覺系統捕捉到輪椅上的顫抖雙手&#xff0c;自動降低語速并調大屏幕字體&#xff1b;識別出老人病歷本上的“心…

【51單片機和數碼管仿真顯示問題共陰共陽代碼】2022-9-24

緣由單片機和數碼管仿真顯示問題-嵌入式-CSDN問答 #include "REG52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f ,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,64,15,56}; //共陰0~F消隱減號 void smxs(unsigned char mz, unsigned c…

Java#包管理器來時的路

不依賴任何Jar包 - HelloWorld.java mkdir demo && cd demo;# HelloWorld.java cat > HelloWorld.java << EOF public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, world!");} } EOF# 編譯class javac …

Android Framework知識點

1 重點知識 1.1 Alarm 當手機重啟或者應用被殺死的時候&#xff0c;Alarm會被刪除&#xff0c;因此&#xff0c;如果想通過Alarm來完成長久定時任務是不可靠的&#xff0c;如果非要完成長久定時任務&#xff0c;可以這樣&#xff1a;將應用的所有Alarm信息存到數據庫中&#xf…

代碼隨想錄算法訓練營Day6 | 哈希表 Part 1

一、今日學習目標 掌握哈希表的核心理論&#xff08;哈希函數、哈希碰撞及解決方法&#xff09;&#xff0c;理解數組、set、map 三種哈希結構的適用場景&#xff0c;并通過「兩個數組的交集」「快樂數」「兩數之和」三道題目&#xff0c;實戰掌握哈希表在快速查找、去重、鍵值…

5.13.樹、森林與二叉樹的轉換

當使用"孩子兄弟表示法"存儲樹或森林時&#xff0c;最終會呈現出與二叉樹類似的形態&#xff0c;所以樹、森林與二叉樹之間的轉換本質上就是畫出采用孩子兄弟表示法存儲的樹和森林。一."樹->二叉樹"的轉換&#xff1a;1.例一&#xff1a;以上述圖片左邊…

Spring 核心流程

Spring 核心流程前言一、AbstractApplicationContext#refresh 方法解析1.1 前置1.2 refresh 方法1.2.1 prepareRefresh1.2.2 obtainFreshBeanFactory1.2.3 prepareBeanFactory1.2.4 postProcessBeanFactory1.2.5 invokeBeanFactoryPostProcessors1.2.6 registerBeanPostProcess…

RS485轉Profinet網關與JRT激光測距傳感器在S7-1200 PLC系統中的技術解析與應用

RS485轉Profinet網關與JRT激光測距傳感器在S7-1200 PLC系統中的技術解析與應用技術核心&#xff1a;協議轉換與數據橋梁在工業自動化系統中&#xff0c;RS485轉Profinet網關承擔著協議翻譯官的角色。以XD-MDPN100型號為例&#xff0c;其本質是將RS485設備的串口數據封裝為Profi…

《C++ string 完全指南:string的模擬實現》

string的模擬實現 文章目錄string的模擬實現一、淺拷貝和深拷貝1.淺拷貝2.深拷貝3.寫時拷貝二、定義string的成員變量三、string的接口實現1.string的默認成員函數&#xff08;1&#xff09;構造函數實現&#xff08;2&#xff09;析構函數實現&#xff08;3&#xff09;拷貝構…

造成服務器內存不足的原因有什么

服務器在日常的運行過程中&#xff0c;會存儲大量關于企業重要的數據信息&#xff0c;偶爾會出現內存飆升空間不足的情況&#xff0c;服務器內存作為服務器數據處理和存儲的主要空間&#xff0c;異常占用會導致服務器性能降低&#xff0c;影響到企業業務的響應速度&#xff0c;…