責任鏈模式 職責鏈模式 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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...