責任鏈模式 職責鏈模式 Chain of Responsibility Pattern 行為型 設計模式(十七)

来源:https://www.cnblogs.com/noteless/archive/2018/12/10/10096228.html
-Advertisement-
Play Games

責任鏈模式是一種行為型模式,將一系列處理者鏈接在一起,形成一個處理整體,將具體的請求處理者與請求者進行分離,本文介紹了職責鏈模式的意圖,使用場景,以及結構,角色模塊,並且給出來了Java版本的責任鏈模式實現。 ...


責任鏈模式(Chain of Responsibility Pattern) 職責鏈模式 image_5c0e046c_2968

意圖

使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係 將這些對象連接成一條鏈,並沿著這條鏈傳遞請求,直到有一個對象處理它為止。   責任鏈模式中,每個對象通過持有對下家的引用而鏈接起來,形成一條鏈條,串聯起來多個處理對象。 在責任鏈模式中,請求在鏈上進行傳遞,直到鏈上的某一個對象決定處理此請求。 發出這個請求的客戶端程式並不知道到底是哪一個對象具體的處理了請求 這使得系統可以在不影響客戶端的情況下,動態的重新組織鏈和增加責任 比如類載入機制中的雙親委派模型,類載入器含有父  類載入器的引用,形成了一個處理鏈 (不過類載入機制的具體行為與責任鏈有出入,會一直委派到最頂級類載入,而不會在中間進行處理然後返回)  

解析

責任鏈模式是一種很常見的場景 比如請假,很多公司會根據請假的原因以及請假的時長,將會有不同的審批人 比如採購,對於採購金額的不同,可能需要不同的審批人   思考下請假的過程,可能你經歷過有兩種形式: 形式一: 第一步寫好請假條;
第二步拿給人事,人事說找部門主管簽字
第三步拿請假條給部門主管,部門主管發現請假三天,然後告訴你,一天以上的假期需要老闆審批
第四步拿請假條給老闆審批
形式二: 第一步寫好請假條交給人事;(人事拿給部門主管,部門主管看情況,如果批不了拿給老闆審批) 第二步等待審批;   可能的兩種流程就是上面這樣子的,請假的流程一般就這樣子了,總共有幾級審批,這幾級審批都有誰負責,也不會輕易的變化 再看一個採購審批的例子 示例 以採購為例演示審批流程 抽象處理人角色 處理人擁有姓名屬性,另外定義了抽象的操作方法
package nonechainresponsibility;
public abstract class Handler {
protected String name;
Handler(String name){
this.name = name;
}
public abstract void operation();
}

 

具體的處理人角色 DepartmentManager和Boss 
package nonechainresponsibility;
public class DepartmentManager extends Handler {
public DepartmentManager(String name){
super(name);
}
@Override
public void operation() {
System.out.println("DepartmentManager process..name: "+this.name);
}
}
package nonechainresponsibility;
public class Boss extends Handler {
public Boss(String name) {
super(name);
}

@Override
public void operation() {
System.out.println("Boss process..name: " + this.name);
}
}
測試代碼如下,流程很簡單: 如果是金額小於1000元,由部門經理DepartmentManager  李四審批 否則,將由老總 Boss  張三  審批 image_5c0e046c_6677   上面的實例中 測試類Test作為客戶端,也就是相當於採購員 需要自行判斷金額的範圍,然後找對應的審批人進行審批 如果說金額從幾百到幾萬到幾十萬,根據金額分別有十幾個主管負責人進行審批呢?那麼審批的邏輯將會變得很複雜 也就是內部將會有更多的選擇邏輯判斷 而且 如果規則變動了呢?你將不得不修改邏輯,進行調整,顯然非常的不利於擴展 即使是新增一個級別的審批,仍舊是需要修改代碼邏輯 另外,如果不同的部門的審批順序略有差異呢?如何應對?   上面的這種處理邏輯的根本問題在於:審批的流程固化在代碼中了 可是你只是個採購員,你希望的只是審批單被批准,你其實並不關心到底是誰審批的,那為什麼你要關註於這麼多的細節呢? 責任鏈模式就是為瞭解決這種類似的場景。   一句話概括責任鏈模式:“能搞就搞,搞不了給下一個人,請求發起者不關心誰幫我處理”

代碼示例

處理人角色新增successor 屬性,用於作為下游處理,提供了setSuccessor()方法進行設置  並且對operation方法進行改變,接受參數(Integer price)
package chainresponsibility;
public abstract class Handler {
protected String name;
Handler(String name){
this.name = name;
}
protected Handler successor;
public void setSuccessor(Handler successor){
this.successor = successor;
}
public abstract void operation(Integer price);
}
部門處理人角色和老闆角色同步做出修改 將判斷邏輯移入到處理內部 如果自己不能處理,那麼請求下游進行處理(示例比較簡單,所以Boss沒搞)
package chainresponsibility;

public class DepartmentManager extends Handler {

public DepartmentManager(String name){
super(name);
}
@Override
public void operation(Integer price) {
if(price < 1000){
System.out.println("DepartmentManager process..name: "+this.name);
}else{
successor.operation(price);
}
}
}
package chainresponsibility;
public class Boss extends Handler {

public Boss(String name) {
super(name);
}

@Override
public void operation(Integer price) {
System.out.println("Boss process..name: " + this.name);
}
}
測試代碼
package chainresponsibility;
public class Test {
public static void main(String[] args){
/*
* 動態生成鏈條
* */
Handler DmHandler = new DepartmentManager("李四");
Handler BossHandler = new Boss("張三");
DmHandler.setSuccessor(BossHandler);

/*
* 調用處理鏈的一個起始端點
* */
DmHandler.operation(600);
DmHandler.operation(6000);
}
}

 

image_5c0e046c_872   重構後的代碼邏輯沒有發生變化---仍舊是處理請求 但是通過引入successor 屬性,以及setSuccessor()方法,可以將下游處理者串聯起來 擁有了下游的引用,如果處理不了就可以將請求向下傳遞 因為每個處理者都需要獨立面對請求,所以將邏輯內置,也就是條件以參數的形式傳遞 通過指向下游處理對象的引用,形成了一條鏈,每次請求只需要請求開始端點位置的對象即可 如果無法處理,會自動請求下游的對象進行處理,客戶端不需要關註 而且,通過引用可以在運行期間動態的組織職責鏈,比如不同部門處理層級不一樣的問題就可以輕易解決 如果增加新的審批層級,只需要新增審批類,並且在創建責任鏈的時候,將新增的審批類添加進去即可 客戶端並不需要發生變動   上面的示例簡單的演示了職責鏈模式的演變過程 簡單說就是每個人職責清晰的獨立劃分開來,然後一個模塊負責組裝生成責任鏈  客戶端將請求發送給責任鏈的起始點即可

結構

image_5c0e046c_6dbf 抽象處理者角色Handler 定義一個處理請求的介面 Handler角色知道“下一個處理者”是誰,如果自己無法處理請求,他會將請求轉發給“下一個處理者”  具體的處理者角色ConcreteHandler 處理請求的具體角色,比如上面示例中的DepartmentManager 客戶端角色Client
Client角色向組裝好的責任鏈的第一個ConcreteHandler發起請求

通過責任鏈模式弱化了請求發起者與請求處理者的聯繫 對於請求者來說,責任鏈上的所有對象就是一個請求處理者,到底具體到哪個類進行出力,請求者並不關心 不存在“某個請求必須要誰處理”這種明確的指向關係,請求者不清楚 專註於自己的工作 每個處理業務的對象都更加專註於自己的工作,如果自己無法處理,那麼交給其他更專業的人 這樣則能保障任務能夠快速高效的完成 責任鏈模式並不創建責任鏈,由系統的其他部分負責創建 示例為了簡單所以在測試主函數中一併創建,應該由系統其他部分進行創建,將責任鏈的連接邏輯與使用解耦 動態改變職責鏈 引用是組合的形式,可以在運行時動態的設定 上面說到責任鏈由系統的其他部分進行創建,他可以動態的完成責任鏈的創建

分類

責任鏈模式分為純粹的責任鏈模式和不純粹的責任鏈模式 純粹的責任鏈模式要求責任鏈中的對象,也就是具體的處理者只能在兩個行為中選擇一個 也就是要麼承擔責任,要麼責任轉交給下家 不允許出現某一個具體的處理者對象承擔了一部分責任之後又把責任向下傳遞。   而通常情況下,我們的實際使用的責任鏈模式很難純粹,基本都會摻雜一些處理邏輯 其實這種命名與否的爭論對於使用模式毫無意義,重點在於解決問題  黑貓白貓抓到老鼠就是好貓

總結

如果你的系統中已經存在了一個由多個處理者對象組成的請求處理序列 那麼你就可以考慮將這個請求處理轉換為責任鏈模式 如果有多於一個處理者對象會處理一個請求,但是事先卻並不知道到底誰來處理 這個處理者對象是動態確定的,比如新來一個採購員,他還不熟悉公司規則,不確定到底誰來負責審批他的這個單子 責任鏈顯著的特點就是將請求和處理者進行分離,請求者可以不知道具體是誰處理了請求,將請求與處理進行瞭解耦,提高了系統的靈活性  具體的處理者可能還與責任鏈的組成順序有很大的關係,通過選擇某些處理者組成鏈,以及設置順序,增加了極大的靈活性 責任鏈模式所有的請求都通過責任鏈進行處理,如果鏈比較長,而且需求總是在後端進行處理,勢必會引入一些性能問題 而且,客戶端不知道具體誰處理的,你也不知道,環節較多,調試時也會不方便 而且還需要控制節點的數量,要避免超長鏈的情況,那樣這條鏈將會變成一種負擔,這種會破壞系統的性能   而且還需要註意,如果一個請求從頭走到尾是否一定會有對象對他進行處理?如果沒有被處理也可能是因為沒有正確的配置責任鏈而導致的 如果鏈接生成的有問題,比如環形,並且也沒有設置次數限制,豈不是死迴圈?這些問題都需要在運用時考慮到 對於責任鏈一句話總結:如果一個請求,可能會被多個處理者中的一個處理,考慮責任鏈模式 原文地址:責任鏈模式 職責鏈模式 Chain of Responsibility Pattern 行為型 設計模式(十七)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vue route transition vue router 切換動畫 特性 模擬前進後退動畫 基於css3流暢動畫 基於sessionStorage,頁面刷新不影響路由記錄 路由懶載入,返回可記錄滾動條位置 前進後退的判斷與路由路徑規則無關 支持任意基於Vue的UI框架 demo 手機掃碼 "在 ...
  • // 簡訊提交驗證 //$(function(){ var daoshu = 60; var timer = null; //手機號 function checkPhone(){ var phoneNumber = $.trim($('#t_code').val()); var phone = do ...
  • var time = new Date(); //當前時間 var year = time.getFullYear();//當前年份 var month = time.getMonth()+1; //當前月份 var Same_day = time.getDate(); //當前月份幾號 var t... ...
  • 1、基礎準備: 先來瞭解下,如何運用js實現select動態添加option。 //1.動態創建select function createSelect(){ var mySelect = document.createElement("select"); mySelect.id = "mySele ...
  • Bootstrap -- 文本,背景,其他樣式 1. 文本樣式:展示了不同的文本顏色 使用文本樣式: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> ...
  • 1. 你能描述一下漸進增強和優雅降級之間的不同嗎? 2. 請說說瀏覽器內核的組成? 3. 為什麼利用多個功能變數名稱來請求網路資源會更有效? 4. 說說前端開發中, 如何進行性能優化? 5. 從前端角度出發, 談談做好網站seo需要考慮什麼? ...
  • 調用 // 分頁import pages from 'components/common/pages/pages' components: { pages }, <pages :total="total" :page-size="pageSize" @handleSizeChangeSub="han ...
  • 微服務是Devops場景下熱門的開發框架,在大型項目中被廣泛採用。它把一個大型的單個應用程式和服務拆分為數十個的支持微服務,獨立部署、互相隔離,通過擴展組件來處理功能瓶頸問題,比傳統的應用程式更能有效利用計算資源。微服務之間無需關心對方的模型,它通過事先約定好的介面進行數據流轉,使業務可以高效響應市 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...