責任鏈模式是一種行為型模式,將一系列處理者鏈接在一起,形成一個處理整體,將具體的請求處理者與請求者進行分離,本文介紹了職責鏈模式的意圖,使用場景,以及結構,角色模塊,並且給出來了Java版本的責任鏈模式實現。 ...
責任鏈模式(Chain of Responsibility Pattern) 職責鏈模式
意圖
使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係 將這些對象連接成一條鏈,並沿著這條鏈傳遞請求,直到有一個對象處理它為止。 責任鏈模式中,每個對象通過持有對下家的引用而鏈接起來,形成一條鏈條,串聯起來多個處理對象。 在責任鏈模式中,請求在鏈上進行傳遞,直到鏈上的某一個對象決定處理此請求。 發出這個請求的客戶端程式並不知道到底是哪一個對象具體的處理了請求 這使得系統可以在不影響客戶端的情況下,動態的重新組織鏈和增加責任 比如類載入機制中的雙親委派模型,類載入器含有父 類載入器的引用,形成了一個處理鏈 (不過類載入機制的具體行為與責任鏈有出入,會一直委派到最頂級類載入,而不會在中間進行處理然後返回)解析
責任鏈模式是一種很常見的場景 比如請假,很多公司會根據請假的原因以及請假的時長,將會有不同的審批人 比如採購,對於採購金額的不同,可能需要不同的審批人 思考下請假的過程,可能你經歷過有兩種形式: 形式一: 第一步你寫好請假條;第二步你拿給人事,人事說找部門主管簽字
第三步你拿請假條給部門主管,部門主管發現請假三天,然後告訴你,一天以上的假期需要老闆審批
第四步你拿請假條給老闆審批 形式二: 第一步你寫好請假條交給人事;(人事拿給部門主管,部門主管看情況,如果批不了拿給老闆審批) 第二步等待審批; 可能的兩種流程就是上面這樣子的,請假的流程一般就這樣子了,總共有幾級審批,這幾級審批都有誰負責,也不會輕易的變化 再看一個採購審批的例子 示例 以採購為例演示審批流程 抽象處理人角色 處理人擁有姓名屬性,另外定義了抽象的操作方法
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 張三 審批 上面的實例中 測試類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); } }
重構後的代碼邏輯沒有發生變化---仍舊是處理請求 但是通過引入successor 屬性,以及setSuccessor()方法,可以將下游處理者串聯起來 擁有了下游的引用,如果處理不了就可以將請求向下傳遞 因為每個處理者都需要獨立面對請求,所以將邏輯內置,也就是條件以參數的形式傳遞 通過指向下游處理對象的引用,形成了一條鏈,每次請求只需要請求開始端點位置的對象即可 如果無法處理,會自動請求下游的對象進行處理,客戶端不需要關註 而且,通過引用可以在運行期間動態的組織職責鏈,比如不同部門處理層級不一樣的問題就可以輕易解決 如果增加新的審批層級,只需要新增審批類,並且在創建責任鏈的時候,將新增的審批類添加進去即可 客戶端並不需要發生變動 上面的示例簡單的演示了職責鏈模式的演變過程 簡單說就是每個人職責清晰的獨立劃分開來,然後一個模塊負責組裝生成責任鏈 客戶端將請求發送給責任鏈的起始點即可
結構
抽象處理者角色Handler 定義一個處理請求的介面 Handler角色知道“下一個處理者”是誰,如果自己無法處理請求,他會將請求轉發給“下一個處理者” 具體的處理者角色ConcreteHandler 處理請求的具體角色,比如上面示例中的DepartmentManager 客戶端角色ClientClient角色向組裝好的責任鏈的第一個ConcreteHandler發起請求
通過責任鏈模式弱化了請求發起者與請求處理者的聯繫 對於請求者來說,責任鏈上的所有對象就是一個請求處理者,到底具體到哪個類進行出力,請求者並不關心 不存在“某個請求必須要誰處理”這種明確的指向關係,請求者不清楚 專註於自己的工作 每個處理業務的對象都更加專註於自己的工作,如果自己無法處理,那麼交給其他更專業的人 這樣則能保障任務能夠快速高效的完成 責任鏈模式並不創建責任鏈,由系統的其他部分負責創建 示例為了簡單所以在測試主函數中一併創建,應該由系統其他部分進行創建,將責任鏈的連接邏輯與使用解耦 動態改變職責鏈 引用是組合的形式,可以在運行時動態的設定 上面說到責任鏈由系統的其他部分進行創建,他可以動態的完成責任鏈的創建
分類
責任鏈模式分為純粹的責任鏈模式和不純粹的責任鏈模式 純粹的責任鏈模式要求責任鏈中的對象,也就是具體的處理者只能在兩個行為中選擇一個 也就是要麼承擔責任,要麼責任轉交給下家 不允許出現某一個具體的處理者對象承擔了一部分責任之後又把責任向下傳遞。 而通常情況下,我們的實際使用的責任鏈模式很難純粹,基本都會摻雜一些處理邏輯 其實這種命名與否的爭論對於使用模式毫無意義,重點在於解決問題 黑貓白貓抓到老鼠就是好貓總結
如果你的系統中已經存在了一個由多個處理者對象組成的請求處理序列 那麼你就可以考慮將這個請求處理轉換為責任鏈模式 如果有多於一個處理者對象會處理一個請求,但是事先卻並不知道到底誰來處理 這個處理者對象是動態確定的,比如新來一個採購員,他還不熟悉公司規則,不確定到底誰來負責審批他的這個單子 責任鏈顯著的特點就是將請求和處理者進行分離,請求者可以不知道具體是誰處理了請求,將請求與處理進行瞭解耦,提高了系統的靈活性 具體的處理者可能還與責任鏈的組成順序有很大的關係,通過選擇某些處理者組成鏈,以及設置順序,增加了極大的靈活性 責任鏈模式所有的請求都通過責任鏈進行處理,如果鏈比較長,而且需求總是在後端進行處理,勢必會引入一些性能問題 而且,客戶端不知道具體誰處理的,你也不知道,環節較多,調試時也會不方便 而且還需要控制節點的數量,要避免超長鏈的情況,那樣這條鏈將會變成一種負擔,這種會破壞系統的性能 而且還需要註意,如果一個請求從頭走到尾是否一定會有對象對他進行處理?如果沒有被處理也可能是因為沒有正確的配置責任鏈而導致的 如果鏈接生成的有問題,比如環形,並且也沒有設置次數限制,豈不是死迴圈?這些問題都需要在運用時考慮到 對於責任鏈一句話總結:如果一個請求,可能會被多個處理者中的一個處理,考慮責任鏈模式 原文地址:責任鏈模式 職責鏈模式 Chain of Responsibility Pattern 行為型 設計模式(十七)