設計模式(十二)責任鏈模式
一、擊鼓傳花
擊鼓傳花是一種熱鬧而又緊張的飲酒遊戲。在酒宴上賓客一次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。
比如說,賈母、賈赦、賈政、賈寶玉和賈環是五個參加擊鼓傳花遊戲的傳花者,他們組成一個環鏈。擊鼓者將花傳給賈母,開始傳花遊戲。花由賈母傳給賈赦,賈赦傳給賈政,賈政傳給賈寶玉,賈寶玉傳給賈環,賈環再傳給賈母,由此往複,如所示。
擊鼓傳花便是一種典型的責任鏈模式。
二、什麼是責任鏈模式
責任鏈模式是一種對象的行為模式。在責任鏈模式裡,很多個物件由每一個對象對其下家的引用而串連起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的用戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響用戶端的情況下動態地重新組織和分配責任。
三、責任鏈模式的結構
● 抽象處理者(Handler)角色:定義出一個處理請求的介面。如果需要,介面可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類別或者Java介面實現。中Handler類的彙總關係給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。
● 具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
四、責任鏈模式的執行個體
我為更清楚的看出責任鏈模式的結構,我們先來看一看最簡答的代碼實現。
package com.designpattern.pre1;/** * 抽象處理者,定義處理者的介面 * * @author 98583 * */public abstract class Handler { /** * 下一個處理者 */ protected Handler successor; /** * 每個處理者的處理方法 */ public abstract void handleRequest(); /** * 設定下一個處理者 * * @param successor */ public void setSuccessor(Handler successor) { this.successor = successor; } /** * 擷取下一個處理者 * * @return */ public Handler getSuccessor() { return successor; }}
package com.designpattern.pre1;/** * 具體處理者 * * @author 98583 * */public class ConcreteHandler extends Handler { /** * 處理方法 */ public void handleRequest() { /** * 如果有下一個處理者就交給下一個處理者,但是實際情況中應該判斷這個處理者能否處理這個問題,不能處理才傳給下一個處理者 */ if (getSuccessor() != null) { System.out.println(The request is passed to + getSuccessor()); getSuccessor().handleRequest(); }// 在這個處理者中處理 else { System.out.println(The request is handled here.); } }}
package com.designpattern.pre1;public class Client { static private Handler handler1, handler2; public static void main(String[] args) { /** * 定義兩個處理者 */ handler1 = new ConcreteHandler(); handler2 = new ConcreteHandler(); handler1.setSuccessor(handler2); handler1.handleRequest(); }}
現在瞭解了責任鏈模式的基本結構,我們可以來實現上邊的紅樓夢中的擊鼓傳花的故事了。
package com.designpattern.chainofresp;/** * 相當於抽象的Handler * @author 98583 * */abstract class Player { abstract public void handle(int i); /** * 下一位處理者 */ private Player successor; public Player() { successor = null; } /** * 設定下一個處理者 * @param aSuccessor */ protected void setSuccessor(Player aSuccessor) { successor = aSuccessor; } /** * 傳給下一個處理者,這個方法本不應該出現在這,因為每個處理者中都有這個方法,所以就放到父類中來了 * @param index */ public void next(int index) { if (successor != null) { successor.handle(index); } else { System.out.println(Program terminated.); } }}
package com.designpattern.chainofresp;/** * 賈母的類相當於ConcreteHandler * @author 98583 * */class JiaMu extends Player { public JiaMu(Player aSuccessor) { this.setSuccessor(aSuccessor); } /** * 這個處理和的處理方法 */ public void handle(int i) { if (i == 1) { System.out.println(Jia Mu gotta drink!); } else { System.out.println(Jia Mu passed!); /** * 傳給下一個處理者 */ next(i); } }}
其他人的類與賈母的類相似,不再貼出,最後附上源碼。
package com.designpattern.chainofresp;/** * 擊鼓者,相當於用戶端 * @author 98583 * */public class DrumBeater { private static Player player; static public void main(String[] args) { JiaMu jiaMu = new JiaMu(null); jiaMu.setSuccessor( new JiaShe ( new JiaZheng( new JiaBaoYu( new JiaHuan( jiaMu ) ) ) ) ); player = jiaMu; player.handle(4); }}
五、責任鏈模式
優點:
降低耦合度 可簡化對象的相互串連 增強給對象指派職責的靈活性 增加新的請求處理類很方便
缺點:
不能保證請求一定被接收 系統效能將受到一定影響,而且在進行代碼調試時不太方便;可能會造成迴圈調用
六、適用環境
1、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
2、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
3、可動態指定一組對象處理請求