第16章 責任鏈模式
16.1 古代婦女的枷鎖——“三從四德”
中國古代對婦女制定了“三從四德”的道德規范,“三從”是指“未嫁從父、既嫁從夫、夫死從子”。也就是說,一位女性在結婚之前要聽從于父親,結婚之后要聽從于丈夫,如果丈夫死了還要聽從于兒子。舉例來說,如果一位女性要出去逛街,在她出嫁前必須征得父親的同意,出嫁之后必須獲得丈夫的許可,那丈夫死了怎么辦?那就得問問兒子是否允許自己出去逛街。估計你接下來馬上要問:“要是沒有兒子怎么辦?”那就請示小叔子、侄子等。在父系社會中,婦女只占從屬地位,現在想想中國古代的婦女還是挺悲慘的,連逛街都要多番請示。作為父親、丈夫或兒子,只有兩種選擇:要不承擔起責任來,允許她或不允許她逛街;要不就讓她請示下一個人,這是整個社會體系的約束,應用到我們項目中就是業務規則。下面來看如何通過程序來實現“三從”,需求很簡單:通過程序描述一下古代婦女的“三從”制度。好,我們先來看類圖,如圖16-1所示。

圖16-1 婦女“三從”類圖
類圖非常簡單,IHandler是三個有決策權對象的接口,IWomen是女性的代碼,其實現也非常簡單,IWomen如代碼清單16-1所示。
代碼清單16-1 女性接口
public?interface?IWomen?{
?????//獲得個人狀況
?????public?int?getType();
?????//獲得個人請示,你要干什么?出去逛街?約會?還是看電影?
?????public?String?getRequest();
}
女性接口僅兩個方法,一個是取得當前的個人狀況getType,通過返回值決定是結婚了還是沒結婚、丈夫是否在世等,另外一個方法getRequest是要請示的內容,要出去逛街還是吃飯,其實現類如代碼清單16-2所示。
代碼清單16-2 古代婦女
public?class?Women?implements?IWomen{
?????/*
??????*?通過一個int類型的參數來描述婦女的個人狀況
??????*?1--未出嫁
??????*?2--出嫁
??????*?3--夫死
??????*/
?????private?int?type=0;
?????//婦女的請示
?????private?String?request?=?"";
?????//構造函數傳遞過來請求
?????public?Women(int?_type,String?_request){
?????????????this.type?=?_type;
?????????????this.request?=?_request;
?????}
?????//獲得自己的狀況
?????public?int?getType(){
?????????????return?this.type;
?????}
?????//獲得婦女的請求
?????public?String?getRequest(){
?????????????return?this.request;
?????}
}
我們使用數字來代表女性的不同狀態:1是未結婚;2是已經結婚的,而且丈夫健在;3是丈夫去世了。從整個設計上分析,有處理權的人(如父親、丈夫、兒子)才是設計的核心,他們是要處理這些請求的,我們來看有處理權的人員接口IHandler,如代碼清單16-3所示。
代碼清單16-3 有處理權的人員接口
public?interface?IHandler?{
?????//一個女性(女兒、妻子或者母親)要求逛街,你要處理這個請求
?????public?void?HandleMessage(IWomen?women);
}
非常簡單,有處理權的人對婦女的請求進行處理,分別有三個實現類,在女兒沒有出嫁之前父親是有決定權的,其實現類如代碼清單16-4所示。
代碼清單16-4 父親類
public?class?Father?implements?IHandler?{
?????//未出嫁的女兒來請示父親
?????public?void?HandleMessage(IWomen?women)?{
?????????????System.out.println("女兒的請示是:"+women.getRequest());
?????????????System.out.println("父親的答復是:同意");
?????}
}
在女性出嫁后,丈夫有決定權,如代碼清單16-5所示。
代碼清單16-5 丈夫類
public?class?Husband?implements?IHandler?{
?????//妻子向丈夫請示
?????public?void?HandleMessage(IWomen?women)?{
?????????????System.out.println("妻子的請示是:"+women.getRequest());
?????????????System.out.println("丈夫的答復是:同意");
?????}
}
在女性喪偶后,對母親提出的請求兒子有決定權,如代碼清單16-6所示。
代碼清單16-6 兒子類
public?class?Son?implements?IHandler?{
?????//母親向兒子請示
?????public?void?HandleMessage(IWomen?women)?{
?????????????System.out.println("母親的請示是:"+women.getRequest());
?????????????System.out.println("兒子的答復是:同意");
?????}
}
以上三個實現類非常簡單,只有一個方法,處理女兒、妻子、母親提出的請求,我們來模擬一下一個古代婦女出去逛街是如何請示的,如代碼清單16-7所示。
代碼清單16-7 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//隨機挑選幾個女性
?????????????Random?rand?=?new?Random();
?????????????ArrayList<IWomen>?arrayList?=?new?ArrayList();
?????????????for(int?i=0;i<5;i++){
?????????????????????arrayList.add(new?Women(rand.nextInt(4),"我要出去逛街"));
?????????????}
?????????????//定義三個請示對象
?????????????IHandler?father?=?new?Father();
?????????????IHandler?husband?=?new?Husband();
?????????????IHandler?son?=?new?Son();
?????????????for(IWomen?women:arrayList){
?????????????????????if(women.getType()?==1){?//未結婚少女,請示父親
????????????????????????????System.out.println("\n--------女兒向父親請示-------");
????????????????????????????father.HandleMessage(women);
?????????????????????}else?if(women.getType()?==2){??//已婚少婦,請示丈夫
????????????????????????????System.out.println("\n--------妻子向丈夫請示-------");
????????????????????????????husband.HandleMessage(women);
?????????????????????}else?if(women.getType()?==?3){?//母親請示兒子
????????????????????????????System.out.println("\n--------母親向兒子請示-------");
????????????????????????????son.HandleMessage(women);
?????????????????????}else{
????????????????????????????//暫時什么也不做
?????????????????????}
?????????????}
?????}
}
首先是通過隨機方法產生了5個古代婦女的對象,然后看她們是如何就逛街這件事去請示的,運行結果如下所示(由于是隨機的,您看到的結果可能和這里有所不同):
--------女兒向父親請示-------
女兒的請示是:我要出去逛街
父親的答復是:同意
--------母親向兒子請示-------
母親的請示是:我要出去逛街
兒子的答復是:同意
--------妻子向丈夫請示-------
妻子的請示是:我要出去逛街
丈夫的答復是:同意
--------女兒向父親請示-------
女兒的請示是:我要出去逛街
父親的答復是:同意
“三從四德”的舊社會規范已經完整地表現出來了,你看誰向誰請示都定義出來了,但是你是不是發現這個程序寫得有點不舒服?有點別扭?有點想重構它的感覺?那就對了!這段代碼有以下幾個問題:
● 職責界定不清晰
對女兒提出的請示,應該在父親類中做出決定,父親有責任、有義務處理女兒的請示,因此Father類應該是知道女兒的請求自己處理,而不是在Client類中進行組裝出來,也就是說原本應該是父親這個類做的事情拋給了其他類進行處理,不應該是這樣的。
● 代碼臃腫
我們在Client類中寫了if...else的判斷條件,而且能隨著能處理該類型的請示人員越多,if...else的判斷就越多,想想看,臃腫的條件判斷還怎么有可讀性?!
● 耦合過重
這是什么意思呢,我們要根據Women的type來決定使用IHandler的那個實現類來處理請求。有一個問題是:如果IHandler的實現類繼續擴展怎么辦?修改Client類?與開閉原則違背了!
● 異常情況欠考慮
妻子只能向丈夫請示嗎?如果妻子(比如一個現代女性穿越到古代了,不懂什么“三從四德”)向自己的父親請示了,父親應該做何處理?我們的程序上可沒有體現出來,邏輯失敗了!
既然有這么多的問題,那我們要想辦法來解決這些問題,我們先來分析一下需求,女性提出一個請示,必然要獲得一個答復,甭管是同意還是不同意,總之是要一個答復的,而且這個答復是唯一的,不能說是父親作出一個決斷,而丈夫也作出了一個決斷,也即是請示傳遞出去,必然有一個唯一的處理人給出唯一的答復,OK,分析完畢,收工,重新設計,我們可以抽象成這樣一個結構,女性的請求先發送到父親類,父親類一看是自己要處理的,就作出回應處理,如果女兒已經出嫁了,那就要把這個請求轉發到女婿來處理,那女婿一旦去天國報道了,那就由兒子來處理這個請求,類似于如圖16-2所示的順序處理圖。

圖16-2 女性請示的順序處理圖
父親、丈夫、兒子每個節點有兩個選擇:要么承擔責任,做出回應;要么把請求轉發到后序環節。結構分析得已經很清楚了,那我們看怎么來實現這個功能,類圖重新修正,如圖16-3所示。

圖16-3 順序處理的類圖
從類圖上看,三個實現類Father、Husband、Son只要實現構造函數和父類中的抽象方法response就可以了,具體由誰處理女性提出的請求,都已經轉移到了Handler抽象類中,我們來看Handler怎么實現,如代碼清單16-8所示。
代碼清單16-8 修改后的Handler類
public?abstract?class?Handler?{
?????public?final?static?int?FATHER_LEVEL_REQUEST?=?1;
?????public?final?static??int?HUSBAND_LEVEL_REQUEST?=?2;
?????public?final?static??int?SON_LEVEL_REQUEST?=?3;
?????//能處理的級別
?????private?int?level?=0;??????
?????//責任傳遞,下一個人責任人是誰
?????private?Handler?nextHandler;
?????//每個類都要說明一下自己能處理哪些請求
?????public?Handler(int?_level){
?????????????this.level?=?_level;
?????}
?????//一個女性(女兒、妻子或者是母親)要求逛街,你要處理這個請求
?????public?final?void?HandleMessage(IWomen?women){
?????????????if(women.getType()?==?this.level){
????????????????????this.response(women);
?????????????}else{
????????????????????if(this.nextHandler?!=?null){??//有后續環節,才把請求往后遞送
??????????????????????????this.nextHandler.HandleMessage(women);
????????????????????}else{?//已經沒有后續處理人了,不用處理了
??????????????????????????System.out.println("---沒地方請示了,按不同意處理---\n");
????????????????????}
?????????????}
?????}
?????/*
??????*?如果不屬于你處理的請求,你應該讓她找下一個環節的人,如女兒出嫁了,
??????*?還向父親請示是否可以逛街,那父親就應該告訴女兒,應該找丈夫請示
??????*/
?????public?void?setNext(Handler?_handler){
?????????????this.nextHandler?=?_handler;
?????}
?????//有請示那當然要回應
?????protected?abstract?void?response(IWomen?women);
}
方法比較長,但是還是比較簡單的,讀者有沒有看到,其實在這里也用到模板方法模式,在模板方法中判斷請求的級別和當前能夠處理的級別,如果相同則調用基本方法,做出反饋;如果不相等,則傳遞到下一個環節,由下一環節做出回應,如果已經達到環節結尾,則直接做不同意處理。基本方法response需要各個實現類實現,每個實現類只要實現兩個職責:一是定義自己能夠處理的等級級別;二是對請求做出回應,我們首先來看首節點Father類,如代碼清單16-9所示。
代碼清單16-9 父親類
public?class?Father?extends?Handler?{
?????//父親只處理女兒的請求
?????public?Father(){
?????????????super(Handler.FATHER_LEVEL_REQUEST);
?????}
?????//父親的答復
?????protected?void?response(IWomen?women)?{
?????????????System.out.println("--------女兒向父親請示-------");
?????????????System.out.println(women.getRequest());
?????????????System.out.println("父親的答復是:同意\n");
?????}
}
丈夫類定義自己能處理的等級為2的請示,如代碼清單16-10所示。
代碼清單16-10 丈夫類
public?class?Husband?extends?Handler?{
?????//丈夫只處理妻子的請求
?????public?Husband(){
?????????????super(Handler.HUSBAND_LEVEL_REQUEST);
?????}
?????//丈夫請示的答復
?????protected?void?response(IWomen?women)?{
?????????????System.out.println("--------妻子向丈夫請示-------");
?????????????System.out.println(women.getRequest());
?????????????System.out.println("丈夫的答復是:同意\n");
?????}
}
兒子類只能處理等級為3的請示,如代碼清單16-11所示。
代碼清單16-11 兒子類
public?class?Son?extends?Handler?{
?????//兒子只處理母親的請求
?????public?Son(){
?????????????super(Handler.SON_LEVEL_REQUEST);
?????}
?????//兒子的答復
?????protected?void?response(IWomen?women)?{
?????????????System.out.println("--------母親向兒子請示-------");
?????????????System.out.println(women.getRequest());
?????????????System.out.println("兒子的答復是:同意\n");
?????}
}
這三個類都很簡單,構造方法是必須實現的,父類框定子類必須有一個顯式構造函數,子類不實現編譯不通過。通過構造方法我們設置了各個類能處理的請求類型,Father只能處理請求類型為1(也就是女兒)的請求;Husband只能處理請求類型類為2(也就是妻子)的請求,兒子只能處理請求類型為3(也就是母親)的請求,那如果請求類型為4的該如何處理呢?在Handler中我們已經判斷了,如何沒有相應的處理者(也就是沒有下一環節),則視為不同意。
Women類的接口沒有任何變化,請參考圖16-1所示。
實現類稍微有些變化,如代碼清單16-12所示。
代碼清單16-12 女性類
public?class?Women?implements?IWomen{
?????/*
??????*?通過一個int類型的參數來描述婦女的個人狀況
??????*?1--未出嫁
??????*?2--出嫁
??????*?3--夫死
??????*/
?????private?int?type=0;
?????//婦女的請示
?????private?String?request?=?"";
?????//構造函數傳遞過來請求
?????public?Women(int?_type,String?_request){
?????????????this.type?=?_type;?????????
?????????????//為了便于顯示,在這里做了點處理
?????????????switch(this.type){
?????????????case?1:
?????????????????????????this.request?=?"女兒的請求是:"?+?_request;
?????????????????????????break;
?????????????case?2:
?????????????????????????this.request?=?"妻子的請求是:"?+?_request;
?????????????????????????break;
?????????????case?3:
?????????????????????????this.request?=?"母親的請求是:"?+?_request;
?????????????}
?????}
?????//獲得自己的狀況
?????public?int?getType(){
?????????????return?this.type;
?????}
?????//獲得婦女的請求
?????public?String?getRequest(){
?????????????return?this.request;
?????}
}
為了展示結果清晰一點,Women類做了一些改變,如粗體部分所示。我們再來看Client類是怎么描述古代這一個禮節的,如代碼清單16-13所示。
代碼清單16-13 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//隨機挑選幾個女性
?????????????Random?rand?=?new?Random();
?????????????ArrayList<IWomen>?arrayList?=?new?ArrayList();
?????????????for(int?i=0;i<5;i++){
????????????????????arrayList.add(new?Women(rand.nextInt(4),"我要出去逛街"));
?????????????}
?????????????//定義三個請示對象
?????????????Handler?father?=?new?Father();
?????????????Handler?husband?=?new?Husband();
?????????????Handler?son?=?new?Son();
?????????????//設置請示順序
?????????????father.setNext(husband);
?????????????husband.setNext(son);
?????????????for(IWomen?women:arrayList){
????????????????????father.HandleMessage(women);
?????????????}
?????}
}
在Client中設置請求的傳遞順序,先向父親請示,不是父親應該解決的問題,則由父親傳遞到丈夫類解決,若不是丈夫類解決的問題則傳遞到兒子類解決,最終的結果必然有一個返回,其運行結果如下所示。
--------妻子向丈夫請示-------
妻子的請求是:我要出去逛街
丈夫的答復是:同意
--------女兒向父親請示-------
女兒的請求是:我要出去逛街
父親的答復是:同意
--------母親向兒子請示-------
母親的請求是:我要出去逛街
兒子的答復是:同意
--------妻子向丈夫請示-------
妻子的請求是:我要出去逛街
丈夫的答復是:同意
--------母親向兒子請示-------
母親的請求是:我要出去逛街
兒子的答復是:同意
結果也正確,業務調用類Client也不用去做判斷到底是需要誰去處理,而且Handler抽象類的子類可以繼續增加下去,只需要擴展傳遞鏈而已,調用類可以不用了解變化過程,甚至是誰在處理這個請求都不用知道。在這種模式下,即使現代社會的一個小太妹穿越到古代(例如掉入時空隧道,或者時空突然扭轉,甚至是突然魔法顯靈),對“三從四德”沒有任何了解也可以自由地應付,反正只要請示父親就可以了,該父親處理就父親處理,不該父親處理就往下傳遞。這就是責任鏈模式。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——6大設計原則全新解讀
- 第1章 單一職責原則
- 1.2 絕殺技,打破你的傳統思維
- 1.3 我單純,所以我快樂
- 1.4 最佳實踐
- 第2章 里氏替換原則
- 2.2 糾紛不斷,規則壓制
- 2.3 最佳實踐
- 第3章 依賴倒置原則
- 3.2 言而無信,你太需要契約
- 3.3 依賴的三種寫法
- 3.4 最佳實踐
- 第4章 接口隔離原則
- 4.2 美女何其多,觀點各不同
- 4.3 保證接口的純潔性
- 4.4 最佳實踐
- 第5章 迪米特法則
- 5.2 我的知識你知道得越少越好
- 5.3 最佳實踐
- 第6章 開閉原則
- 6.2 開閉原則的廬山真面目
- 6.3 為什么要采用開閉原則
- 6.4 如何使用開閉原則
- 6.5 最佳實踐
- 第二部分 真刀實槍 ——23種設計模式完美演繹
- 第7章 單例模式
- 7.2 單例模式的定義
- 7.3 單例模式的應用
- 7.4 單例模式的擴展
- 7.5 最佳實踐
- 第8章 工廠方法模式
- 8.2 工廠方法模式的定義
- 8.3 工廠方法模式的應用
- 8.4 工廠方法模式的擴展
- 8.5 最佳實踐
- 第9章 抽象工廠模式
- 9.2 抽象工廠模式的定義
- 9.3 抽象工廠模式的應用
- 9.4 最佳實踐
- 第10章 模板方法模式
- 10.2 模板方法模式的定義
- 10.3 模板方法模式的應用
- 10.4 模板方法模式的擴展
- 10.5 最佳實踐
- 第11章 建造者模式
- 11.2 建造者模式的定義
- 11.3 建造者模式的應用
- 11.4 建造者模式的擴展
- 11.5 最佳實踐
- 第12章 代理模式
- 12.2 代理模式的定義
- 12.3 代理模式的應用
- 12.4 代理模式的擴展
- 12.5 最佳實踐
- 第13章 原型模式
- 13.2 原型模式的定義
- 13.3 原型模式的應用
- 13.4 原型模式的注意事項
- 13.5 最佳實踐
- 第14章 中介者模式
- 14.2 中介者模式的定義
- 14.3 中介者模式的應用
- 14.4 中介者模式的實際應用
- 14.5 最佳實踐
- 第15章 命令模式
- 15.2 命令模式的定義
- 15.3 命令模式的應用
- 15.4 命令模式的擴展
- 15.5 最佳實踐
- 第16章 責任鏈模式
- 16.2 責任鏈模式的定義
- 16.3 責任鏈模式的應用
- 16.4 最佳實踐
- 第17章 裝飾模式
- 17.2 裝飾模式的定義
- 17.3 裝飾模式應用
- 17.4 最佳實踐
- 第18章 策略模式
- 18.2 策略模式的定義
- 18.3 策略模式的應用
- 18.4 策略模式的擴展
- 18.5 最佳實踐
- 第19章 適配器模式
- 19.2 適配器模式的定義
- 19.3 適配器模式的應用
- 19.4 適配器模式的擴展
- 19.5 最佳實踐
- 第20章 迭代器模式
- 20.2 迭代器模式的定義
- 20.3 迭代器模式的應用
- 20.4 最佳實踐
- 第21章 組合模式
- 21.2 組合模式的定義
- 21.3 組合模式的應用
- 21.4 組合模式的擴展
- 21.5 最佳實踐
- 第22章 觀察者模式
- 22.2 觀察者模式的定義
- 22.3 觀察者模式的應用
- 22.4 觀察者模式的擴展
- 22.5 最佳實踐
- 第23章 門面模式
- 23.2 門面模式的定義
- 23.3 門面模式的應用
- 23.4 門面模式的注意事項
- 23.5 最佳實踐
- 第24章 備忘錄模式
- 24.2 備忘錄模式的定義
- 24.3 備忘錄模式的應用
- 24.4 備忘錄模式的擴展
- 24.5 最佳實踐
- 第25章 訪問者模式
- 25.2 訪問者模式的定義
- 25.3 訪問者模式的應用
- 25.4 訪問者模式的擴展
- 25.5 最佳實踐
- 第26章 狀態模式
- 26.2 狀態模式的定義
- 26.3 狀態模式的應用
- 第27章 解釋器模式
- 27.2 解釋器模式的定義
- 27.3 解釋器模式的應用
- 27.4 最佳實踐
- 第28章 享元模式
- 28.2 享元模式的定義
- 28.3 享元模式的應用
- 28.4 享元模式的擴展
- 28.5 最佳實踐
- 第29章 橋梁模式
- 29.2 橋梁模式的定義
- 29.3 橋梁模式的應用
- 29.4 最佳實踐
- 第三部分 誰的地盤誰做主 ——設計模式PK
- 第30章 創建類模式大PK
- 30.1 工廠方法模式VS建造者模式
- 30.2 抽象工廠模式VS建造者模式
- 第31章 結構類模式大PK
- 31.1 代理模式VS裝飾模式
- 31.2 裝飾模式VS適配器模式
- 第32章 行為類模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS狀態模式
- 32.3 觀察者模式VS責任鏈模式
- 第33章 跨戰區PK
- 33.1 策略模式VS橋梁模式
- 33.2 門面模式VS中介者模式
- 33.3 包裝模式群PK
- 第四部分 完美世界 ——設計模式混編
- 第34章 命令模式+責任鏈模式
- 34.2 混編小結
- 第35章 工廠方法模式+策略模式
- 35.2 混編小結
- 第36章 觀察者模式+中介者模式
- 36.2 混編小結
- 第五部分 擴展篇
- 第37章 MVC框架
- 37.2 最佳實踐
- 第38章 新模式
- 38.1 規格模式
- 38.2 對象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空對象模式
- 附錄 23種設計模式彩圖