[設計模式]職責鏈(責任鏈)模式
github地址:https://github.com/1711680493
如需瞭解更多設計模式,請進入我的設計模式專欄
目錄
可直接跳過此部分
有一段時間沒有寫設計模式方面的文章了,這次因為某種需求使用到了責任鏈模式,於是記錄一下
首先講一下我的需求
我在做一個檔案上傳的功能,也就意味著需要將接收到的資料儲存起來
那麼,儲存在什麼地方呢?
我的專案後續會放到多臺電腦上(多臺電腦跑一個程式),於是需要使用格外的機器來儲存圖片,這時使用到的是網路
但是目前我使用的是一臺電腦,所以儲存到本地,於是我想到了將儲存地址放入配置檔案中
這時,我需要一個解析地址的方法
比如我輸入 http://xxx.xxx 就返回 HTTP 的工具物件,輸入 C:/xxx 就返回本地檔案物件
具體不在乎是什麼物件,只需要能夠輸入(input)/輸出(output) 即可
最開始想到使用直譯器模式,最終發現責任鏈模式更為合適
概述
可以想象一下需求,我們輸入一串字串,然後這個字串依次經過處理類,最終返回指定物件
一個職責鏈就如同其名一樣,一條鏈子,所以都是依次被執行,每一個物件有機會
例如上面的例子,假設有類 HTTP 用來判斷是否為http連結,File 用來判斷是否為本地地址,那麼流程如下
客戶端輸入連結->HTTP處理 不能處理/不是->File處理
↓可以處理 ↓可以處理
返回對應物件 返回對應物件
但是按照上面這樣,比如我增加 https,則會經過 File 在處理https,效能就會降低
於是我想到了分級(樹結構),策略模式+職責鏈模式完成我的需求
就好比 有專門做Java開發的和專門做 C# 開發的,做Java開發的主程接到需求,這需求是用C#開發xxx,就直接把需求丟給C#主程
但是需求是Java的,自己有不會或者不想搞,就丟給別的Java開發
以上,我將此功能寫在了我的Java工具包中 https://github.com/1711680493/ShendiKit 對應 shendi.kit.path.URL
職責鏈模式
職責鏈模式又叫責任鏈模式(Chain of Responsibility)
把請求從鏈中的一個物件傳到下一個物件,直到請求被響應為止,通過這種方式去除物件之間的耦合
定義
為了避免請求傳送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一物件記住其下一個物件的引用而連成的一條鏈,
當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止
在責任鏈模式中,客戶只需要將請求傳送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞過程,所以責任鏈將請求的傳送者和請求的處理者解耦了
責任鏈模式是一種物件行為型模式
優點
降低了物件之間的耦合度,該模式使得一個物件無需知道到底是哪一個物件處理其請求以及鏈的結構,傳送者和接收者也無需擁有對方的明確資訊
增強了系統的可擴充套件性,可以根據需要增加新的請求處理類,滿足開閉原則
增強了給物件指派職責的靈活性,當工作流程發生變化,可以動態地改變鏈內成員或者調動它們的次序,也可動態地新增或者刪除責任
責任鏈簡化了物件之間的連結,每個物件只需保持一個指向其後繼者的引用,不需保持其他所有處理者的引用,這避免了使用眾多if,或者if else語句
責任分擔,每個類只需要處理自己該處理的工作,不該處理的傳遞給下一個物件完成,明確各類的責任範圍,符合類的單一職責原則
缺點
不能保證每個請求一定被處理,由於一個請求沒有明確的接收者,所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理
對比較長的職責鏈,請求的處理可能涉及多個處理物件,系統性能將受到一定影響
職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的複雜性,可能會由於職責鏈的錯誤設定而導致系統出錯,如可能會造成迴圈呼叫
通常情況下,可以通過資料鏈表來實現職責鏈模式的資料結構
結構
抽象處理者角色: 定義處理請求的介面,包含抽象處理方法和一個後繼連結
具體處理者角色: 實現抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉給他的後繼者
客戶類角色: 建立處理鏈,並向鏈頭的具體處理者物件提交請求,它不關心處理細節和請求的傳遞過程
應用場景
有多個物件可以處理一個請求,哪個物件處理該請求由執行時刻自動確定
可動態指定一組物件處理請求,或新增新的處理物件
在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求
擴充套件
- 純的職責鏈模式:一個請求必須被某一個處理者物件接收,且一個具體處理者對某個請求的處理只能採用兩種行為之一,自己處理(承擔責任);把責任推給下家處理;
- 不純的職責鏈模式:允許出現某一個具體處理者物件在承擔了請求的一部分責任後又將剩餘的責任傳給下家的情況,且一個請求可以最終不被任何接收端物件所接收
實戰
通過傳遞字串來演示一下
/**
* 職責鏈模式
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
public class ChainOfResponsibility {
public static void main(String[] args) {
Handler ha = new HandlerA();
Handler hb = new HandlerB();
Handler hbc = new HandlerBC();
ha.h = hb;
hb.h = hbc;
ha.handler("ABC");
}
}
/**
* 抽象處理者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
abstract class Handler {
protected Handler h;
public void setHandler(Handler h) {
this.h = h;
}
public abstract void handler(String req);
}
/**
* 具體處理者,只處理A
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class HandlerA extends Handler {
@Override
public void handler(String req) {
if (req.contains("A")) {
System.out.println("有A");
// 如果還有別的則交給下一個執行
if (req.contains("B") || req.contains("C")) {
if (h != null) h.handler(req);
}
} else {
if (h != null) h.handler(req);
}
}
}
/**
* 具體處理者,只處理AB
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class HandlerB extends Handler {
@Override
public void handler(String req) {
if (req.contains("B")) {
System.out.println("有B");
// 如果還有別的則交給下一個執行
if (req.contains("A") || req.contains("C")) {
if (h != null) h.handler(req);
}
} else {
if (h != null) h.handler(req);
}
}
}
/**
* 具體處理者,只處理BC
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class HandlerBC extends Handler {
@Override
public void handler(String req) {
if (req.contains("BC")) {
System.out.println("有BC");
// 如果還有別的則交給下一個執行
if (req.contains("A") || req.contains("B")) {
if (h != null) h.handler(req);
}
} else {
if (h != null) h.handler(req);
}
}
}
輸出如下
總結
職責鏈模式由客戶端建立處理鏈(將物件建立,給設定下一個鏈是什麼)後呼叫第一個鏈來進行處理