一、引言
初看責任鏈模式,心裡不禁想起了一個以前聽過的相聲:看牙。說的是一個病人看牙的時候,醫生不小心把拔下的一個牙掉進了病人嗓子裡。病人因此樓上樓下的跑了好多科室,最後無果而終。
責任鏈模式就是這種“推卸”責任的模式,你的問題在我這裡能解決我就解決,不行就把你推給另一個對象。至於到底誰解決了這個問題了呢?我管呢!
二、定義與結構
從名字上大概也能猜出這個模式的大概模樣——系統中將會存在多個有類似處理能力的對象。當一個請求觸發後,請求將在這些對象組成的鏈條中傳遞,直到找到最合適的“責任”對象,並進行處理。
《設計模式》中給它的定義如下:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
從定義上可以看出,責任鏈模式的提出是為了“解耦”,以應變系統需求的變更和不明確性。
下面是《設計模式》中給出的適用范圍:
1) 有多個的對象可以處理一個請求,哪個對象處理該請求運行時刻自動確定。
2) 你想在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
3) 可處理一個請求的對象集合應被動態指定。
責任鏈模式真的能給發送者和接收者之間解耦(這好像很神奇)嗎?先來看下它的組成角色。這個問題我會在下面提及。
責任鏈模式由兩個角色組成:
1) 抽象處理者角色(Handler):它定義了一個處理請求的接口。當然對於鏈子的不同實現,也可以在這個角色中實現後繼鏈。
2) 具體處理者角色(Concrete Handler):實現抽象角色中定義的接口,並處理它所負責的請求。如果不能處理則訪問它的後繼者。
至於類圖不放也罷。畢竟就是一個繼承或者實現。
三、純與不純
責任鏈模式的純與不純的區別,就像黑貓、白貓的區別一樣。不要刻意的去使自己的代碼來符合一個模式的公式。只要能夠使代碼降低耦合、提高重用,滿足系統需求並能很好的適應變化就好了。正所謂:管它黑貓白貓,抓住老鼠就是好貓!
純的責任鏈模式,規定一個具體處理者角色只能對請求作出兩種動作:自己處理;傳給下家。不能出現處理了一部分,把剩下的傳給了下家的情況。而且請求在責任鏈中必須被處理,而不能出現無果而終的結局。
反之,則就是不純的責任鏈模式。
不純的責任鏈模式還算是責任鏈模式嗎?比如一個請求被捕獲後,每個具體處理者都嘗試去處理它,不管結果如何都將請求再次轉發。我認為這種方式的實現,算不算是責任鏈模式的一種倒不重要,重要的是我們也能從中體味到責任鏈模式的思想:通過將多個處理者之間建立聯系,來達到請求與具體的某個處理者的解耦。
下面的例子就是采用了上面提到的“不純的責任鏈模式”。
四、舉例
這個例子來源於項目中我剛剛完成的一個小功能點——“代號自動生成器”。在項目中存在很多地方,比如:員工工號、檔案代號,要求客戶在使用時輸入。而這些代號對於一個特定的企業或者類別,往往有一定的規則。因此可以讓用戶在系統參數中維護一定的規則,然後通過“代號自動生成器”來給用戶生成代號。
根據初期需求,用戶代號中往往存在以下幾種變動元素:年份、月份、日期、流水號。由於需求比較簡單,因此考慮到用戶可能存在其他變動元素,所以我打算在“被第一顆子彈擊中”後重構一下現有的結構。下面就是我在頭腦中演繹過的使用責任鏈模式的重構。
這裡只用來說明下責任鏈模式的結構和使用,因此不體現功能細節。
//這是抽象處理者角色
public interface CodeAutoParse {
//這裡就是統一的處理請求使用的接口
String[] generateCode(String moduleCode, int number, String rule,String[] target) throws BaseException;
}
//這個為處理日期使用的具體處理者
public class DateAutoParse implements CodeAutoParse{
//獲取當前時間
private final Calendar currentDate = Calendar.getInstance();
//這裡用來注入下一個處理者,系統中采用的是Spring來管理的
private CodeAutoParse theNextParseOfDate;
public void setTheNextParseOfDate(CodeAutoParse theNextParseOfDate){
this.theNextParseOfDate = theNextParseOfDate ;
}
/*
*實現的處理請求的接口
*這個接口首先判斷用戶定義的格式是否有流水號,有則解析,沒有則跳過
*下傳到下一個處理者
*/
public String[] generateCode(String moduleCode, int number, String rule, String[] target)
throws BaseException {
//這裡省略了處理的業務
……
if(theNextParseOfDate != null)
return theNextParseOfDate.generateCode(moduleCode , number , rule, target)
else
return target;
}
其它具體處理者也是如此的結構,每一個裡面都設置有一個用來存放下一個處理者的引用,不管你有沒有下一個處理者。
其實責任鏈模式本身的結構和使用都沒有什麼,就是一個繼承或者實現。在處理請求的時候,按照規定去調用下一個處理者。但是怎麼來維護這樣一條鏈子呢?
《設計模式》一書中僅僅說必須自己引入它,可以參考使用list或者map來進行注冊。而在上面我使用spring來管理具體處理者角色的引入。當有了新的處理者需要添加的時候,僅僅需要修改下配置文件。
五、其他
責任鏈模式優點,上面已經體現出來了。無非就是降低了耦合、提高了靈活性。但是責任鏈模式可能會帶來一些額外的性能損耗,因為它要從鏈子開頭開始遍歷。