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

責任鏈模式(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發起請求

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

分類

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

總結

若是你的系統中已經存在了一個由多個處理者對象組成的請求處理序列
那麼你就能夠考慮將這個請求處理轉換爲責任鏈模式
若是有多於一個處理者對象會處理一個請求,可是事先卻並不知道到底誰來處理
這個處理者對象是動態肯定的,好比新來一個採購員,他還不熟悉公司規則,不肯定到底誰來負責審批他的這個單子
責任鏈顯著的特色就是將請求和處理者進行分離,請求者能夠不知道具體是誰處理了請求,將請求與處理進行了解耦,提升了系統的靈活性 
具體的處理者可能還與責任鏈的組成順序有很大的關係,經過選擇某些處理者組成鏈,以及設置順序,增長了極大的靈活性
責任鏈模式全部的請求都經過責任鏈進行處理,若是鏈比較長,並且需求老是在後端進行處理,勢必會引入一些性能問題
並且,客戶端不知道具體誰處理的,你也不知道,環節較多,調試時也會不方便
並且還須要控制節點的數量, 要避免超長鏈的狀況,那樣這條鏈將會變成一種負擔,這種會破壞系統的性能  
並且還須要注意,若是一個請求從頭走到尾是否必定會有對象對他進行處理? 若是沒有被處理也多是由於沒有正確的配置責任鏈而致使的
若是連接生成的有問題,好比環形,而且也沒有設置次數限制,豈不是死循環?這些問題都須要在運用時考慮到
對於責任鏈一句話總結:若是一個請求,可能會被多個處理者中的一個處理,考慮責任鏈模式
相關文章
相關標籤/搜索