設計模式:責任鏈模式
一、前言
責任鏈(chain of responsibility)模式很像異常的捕獲和處理,當一個問題發生的時候,當前對象看一下自己是否能夠處理,不能的話將問題拋給自己的上級去處理,但是要注意這里的上級不一定指的是繼承關系的父類,這點和異常的處理是不一樣的。所以可以這樣說,當問題不能解決的時候,將問題交給另一個對象去處理,就這樣一直傳遞下去直至當前對象找不到下線了,處理結束。如下圖所示,處於同等層次的類都繼承自Support類,當當前對象不能處理的時候,會根據預先設定好的傳遞關系將問題交給下一個人,可以說是“近水樓台先得月”,就看有沒有能力了。我們也可以看作是大家在玩一個傳謎語猜謎底的小游戲,按照座位的次序以及規定的順序傳遞,如果一個人能回答的上來游戲就結束,否則繼續向下傳,如果所有人都回答不出來也會結束。這樣或許才是責任鏈的本質,體現出了同等級的概念。
二、代碼
Trouble 類:(數據結構)
1 package zyr.dp.cor; 2 3 public class Trouble { 4 5 private int number; 6 public Trouble( int number){ 7 this.number=number; 8 } 9 public int getNumber() { 10 return number; 11 } 12 public String toString(){ 13 return "問題編號:["+number+"]"; 14 } 15 }
Support 類:(抽象類,使用了模板方法)
1 package zyr.dp.cor; 2 3 public abstract class Support { 4 5 protected abstract boolean resolve(Trouble trouble); 6 7 String name; 8 Support next; 9 10 public Support(String name){ 11 this.name=name; 12 } 13 14 public String toString() { 15 return "對象:<"+name+">"; 16 } 17 18 public Support setAndReturnNext(Support next){ 19 this.next=next; 20 return next; 21 } 22 23 public final void support(Trouble trouble){ 24 if(resolve(trouble)){ 25 done(trouble); 26 }else if(next!=null){ 27 next.support(trouble); 28 }else{ 29 fail(trouble); 30 } 31 } 32 33 protected void fail(Trouble trouble) { 34 System.out.println(this+"解決問題失敗,"+trouble); 35 } 36 37 protected void done(Trouble trouble) { 38 System.out.println(this+"已經解決問題,"+trouble); 39 } 40 41 }
NoSupport 類:
1 package zyr.dp.cor; 2 3 public class NoSupport extends Support { 4 5 public NoSupport(String name) { 6 super(name); 7 } 8 9 protected boolean resolve(Trouble trouble) { 10 return false; 11 } 12 13 }
OddSupport 類:
1 package zyr.dp.cor; 2 3 public class OddSupport extends Support { 4 5 public OddSupport(String name) { 6 super(name); 7 } 8 9 protected boolean resolve(Trouble trouble) { 10 return (trouble.getNumber()%2) == 1 ? true : false; 11 } 12 13 }
SpecialSupport 類:
1 package zyr.dp.cor; 2 3 public class SpecialSupport extends Support { 4 5 public int specialNumber; 6 public SpecialSupport(String name,int specialNumber) { 7 super(name); 8 this.specialNumber= specialNumber; 9 } 10 11 protected boolean resolve(Trouble trouble) { 12 return trouble.getNumber()==specialNumber ? true : false; 13 } 14 15 }
LimitSupport 類:
1 package zyr.dp.cor; 2 3 public class LimitSupport extends Support { 4 5 private int limit; 6 public LimitSupport(String name,int limit) { 7 super(name); 8 this.limit=limit; 9 } 10 11 protected boolean resolve(Trouble trouble) { 12 return trouble.getNumber()<=limit? true : false; 13 } 14 15 }
Main類:
1 package zyr.dp.cor; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 Support limitSupportLess = new LimitSupport("有限支持小",5); 7 Support limitSupportMore = new LimitSupport("有限支持大",15); 8 Support oddSupport = new OddSupport("奇數支持"); 9 Support specialSupport = new SpecialSupport("特定支持",36); 10 Support noSupport = new NoSupport("沒有支持"); 11 limitSupportLess.setAndReturnNext(limitSupportMore).setAndReturnNext(oddSupport).setAndReturnNext(specialSupport).setAndReturnNext(noSupport); 12 System.out.println("===<有限支持小>嘗試解決問題==="); 13 for(int i=0;i<40;i++){ 14 limitSupportLess.support(new Trouble(i)); 15 } 16 System.out.println("===<特定支持>嘗試解決問題==="); 17 for(int i=0;i<40;i++){ 18 specialSupport.support(new Trouble(i)); 19 } 20 21 } 22 23 }
運行結果:

1 ===<有限支持小>嘗試解決問題=== 2 對象:<有限支持小>已經解決問題,問題編號:[0] 3 對象:<有限支持小>已經解決問題,問題編號:[1] 4 對象:<有限支持小>已經解決問題,問題編號:[2] 5 對象:<有限支持小>已經解決問題,問題編號:[3] 6 對象:<有限支持小>已經解決問題,問題編號:[4] 7 對象:<有限支持小>已經解決問題,問題編號:[5] 8 對象:<有限支持大>已經解決問題,問題編號:[6] 9 對象:<有限支持大>已經解決問題,問題編號:[7] 10 對象:<有限支持大>已經解決問題,問題編號:[8] 11 對象:<有限支持大>已經解決問題,問題編號:[9] 12 對象:<有限支持大>已經解決問題,問題編號:[10] 13 對象:<有限支持大>已經解決問題,問題編號:[11] 14 對象:<有限支持大>已經解決問題,問題編號:[12] 15 對象:<有限支持大>已經解決問題,問題編號:[13] 16 對象:<有限支持大>已經解決問題,問題編號:[14] 17 對象:<有限支持大>已經解決問題,問題編號:[15] 18 對象:<沒有支持>解決問題失敗,問題編號:[16] 19 對象:<奇數支持>已經解決問題,問題編號:[17] 20 對象:<沒有支持>解決問題失敗,問題編號:[18] 21 對象:<奇數支持>已經解決問題,問題編號:[19] 22 對象:<沒有支持>解決問題失敗,問題編號:[20] 23 對象:<奇數支持>已經解決問題,問題編號:[21] 24 對象:<沒有支持>解決問題失敗,問題編號:[22] 25 對象:<奇數支持>已經解決問題,問題編號:[23] 26 對象:<沒有支持>解決問題失敗,問題編號:[24] 27 對象:<奇數支持>已經解決問題,問題編號:[25] 28 對象:<沒有支持>解決問題失敗,問題編號:[26] 29 對象:<奇數支持>已經解決問題,問題編號:[27] 30 對象:<沒有支持>解決問題失敗,問題編號:[28] 31 對象:<奇數支持>已經解決問題,問題編號:[29] 32 對象:<沒有支持>解決問題失敗,問題編號:[30] 33 對象:<奇數支持>已經解決問題,問題編號:[31] 34 對象:<沒有支持>解決問題失敗,問題編號:[32] 35 對象:<奇數支持>已經解決問題,問題編號:[33] 36 對象:<沒有支持>解決問題失敗,問題編號:[34] 37 對象:<奇數支持>已經解決問題,問題編號:[35] 38 對象:<特定支持>已經解決問題,問題編號:[36] 39 對象:<奇數支持>已經解決問題,問題編號:[37] 40 對象:<沒有支持>解決問題失敗,問題編號:[38] 41 對象:<奇數支持>已經解決問題,問題編號:[39] 42 ===<特定支持>嘗試解決問題=== 43 對象:<沒有支持>解決問題失敗,問題編號:[0] 44 對象:<沒有支持>解決問題失敗,問題編號:[1] 45 對象:<沒有支持>解決問題失敗,問題編號:[2] 46 對象:<沒有支持>解決問題失敗,問題編號:[3] 47 對象:<沒有支持>解決問題失敗,問題編號:[4] 48 對象:<沒有支持>解決問題失敗,問題編號:[5] 49 對象:<沒有支持>解決問題失敗,問題編號:[6] 50 對象:<沒有支持>解決問題失敗,問題編號:[7] 51 對象:<沒有支持>解決問題失敗,問題編號:[8] 52 對象:<沒有支持>解決問題失敗,問題編號:[9] 53 對象:<沒有支持>解決問題失敗,問題編號:[10] 54 對象:<沒有支持>解決問題失敗,問題編號:[11] 55 對象:<沒有支持>解決問題失敗,問題編號:[12] 56 對象:<沒有支持>解決問題失敗,問題編號:[13] 57 對象:<沒有支持>解決問題失敗,問題編號:[14] 58 對象:<沒有支持>解決問題失敗,問題編號:[15] 59 對象:<沒有支持>解決問題失敗,問題編號:[16] 60 對象:<沒有支持>解決問題失敗,問題編號:[17] 61 對象:<沒有支持>解決問題失敗,問題編號:[18] 62 對象:<沒有支持>解決問題失敗,問題編號:[19] 63 對象:<沒有支持>解決問題失敗,問題編號:[20] 64 對象:<沒有支持>解決問題失敗,問題編號:[21] 65 對象:<沒有支持>解決問題失敗,問題編號:[22] 66 對象:<沒有支持>解決問題失敗,問題編號:[23] 67 對象:<沒有支持>解決問題失敗,問題編號:[24] 68 對象:<沒有支持>解決問題失敗,問題編號:[25] 69 對象:<沒有支持>解決問題失敗,問題編號:[26] 70 對象:<沒有支持>解決問題失敗,問題編號:[27] 71 對象:<沒有支持>解決問題失敗,問題編號:[28] 72 對象:<沒有支持>解決問題失敗,問題編號:[29] 73 對象:<沒有支持>解決問題失敗,問題編號:[30] 74 對象:<沒有支持>解決問題失敗,問題編號:[31] 75 對象:<沒有支持>解決問題失敗,問題編號:[32] 76 對象:<沒有支持>解決問題失敗,問題編號:[33] 77 對象:<沒有支持>解決問題失敗,問題編號:[34] 78 對象:<沒有支持>解決問題失敗,問題編號:[35] 79 對象:<特定支持>已經解決問題,問題編號:[36] 80 對象:<沒有支持>解決問題失敗,問題編號:[37] 81 對象:<沒有支持>解決問題失敗,問題編號:[38] 82 對象:<沒有支持>解決問題失敗,問題編號:[39]
我們可以看到同等級的對象,按照自己被添加的次序來安排,這點非常重要,在實際應用中,我們都是將解答能力最弱的類放到最前面,然后一點點加強,這樣可以使得解答能力比較弱的類有機會去解答,正如我們的例子,如果讓解答能力強的類直接去處理問題,能夠處理就不回傳給下一個了,當然我們也看到這里面有的類能力有限,有的類和其他類的能力有重疊部分,當然也有所有類都解決不了的問題。
通過責任鏈,我們可以將問題與處理問題的對象分離出來,特別適合用於不知道發生的問題到底是什么(有很多選擇)但是又必須處理的情況(按照每個選擇情況設計相應的處理類),這樣就弱化了請求問題的人和回答問題的人的關系,如果不得不讓發出請求的人知道處理請求的人,這樣就不利於代碼的復用,因為需要在請求問題的對象之中調用處理請求的對象,而使用責任鏈模式就能很好的規避這一點,便於將這部分代碼獨立出來,當做組件使用,符合開閉原則。
同樣的我們看到在責任鏈中使用了模板方法,在父類中抽象的定義了將要處理的流程,使得擴充起來非常方便,修改起來也很方便。利用委托的思想,不過這次是自己使用自己,通過責任鏈,其實就是一個鏈表,來處理問題是非常好的一種思想。可以動態地修改責任鏈,同時也使得每個責任鏈上的對象可以專注於自己的問題,這樣思路清晰,便於擴展,但是使用責任鏈模式因為不斷地調用能夠處理某個問題的對象(遍歷鏈表)和直接就能知道誰去處理的方式相比肯定會出現延遲,這就需要一個取舍了。
最后,我們要注意,resolve()的屬性是protected,這樣做的好處是可以讓同一個包或者子類使用該方法,而不會讓其他包的類使用,因為解決問題的方法就應該是子類使用的,這樣可以使得該方法不被誤用。而將失敗和成功方法設為protected,也是可以讓子類去繼承和修改,而不讓其他包中的類使用,對於support()方法就是可以讓其他包使用的,因此使用public,這一點可以看出寫代碼的嚴謹度。
三、總結
責任鏈使用了模板方法和委托的思想構建了一個鏈表,通過遍歷鏈表來一個個詢問鏈表中的每一個節點誰可以處理某件事情,如果某個節點能夠勝任,則直接處理,否則繼續向下傳遞,如果都不能處理next==null,則處理結束。責任鏈會造成處理的時延,但是能夠解耦合,提高可擴展性,使得處理方法的類更專注於處理把自己的事情,便於擴展和改變處理的優先級,應用也非常的廣泛。