第44章

大B:“擊鼓傳花便是責任鏈模式的應用。在責任鏈模式裏,很多的對象由每一個對象對其下家的引用而聯接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得係統可以在不影響客戶端的情況下動態地重新組織鏈和分配責任。”

小A:“哇!這就是責任鏈模式啊?聽起來好像有點複雜喔。”

大B:“其實很簡單的。責任鏈可能是一條直線、一個環鏈甚至一個樹結構的一部分。”

小A:“這樣一說好像很簡單,但聽起來好像很複雜似的。”

小A:“那你剛才說的紅樓夢中擊鼓傳花的故事不是就是符合責任鏈模式嗎?”

大B:“顯然,擊鼓傳花符合責任鏈模式的定義。參加遊戲的人是一個個的具體處理者對象,擊鼓的人便是客戶端對象。花代表酒令,是傳向處理者的請求,每一個參加遊戲的人在接到傳來的花時,可選擇的行為隻有兩個:一是將花向下傳;一是執行酒令——喝酒。一個人不能既執行酒令,又向下家傳花;當某一個人執行了酒令之後,遊戲重新開始。擊鼓的人並不知道最終是由哪一個做遊戲的人執行酒令,當然執行酒令的人必然是做遊戲的人們中的一個。”

大B:“單獨考慮擊鼓傳花係統,那麽像賈母、賈赦、賈政、賈寶玉和賈環等傳花者均應當是‘具體傳花者’的對象,而不應當是單獨的類;但是責任鏈模式往往是建立在現有係統的基礎之上的,因此鏈的結構和組成不由責任鏈模式本身決定。”

小A:“喔!是嗎?”

大B:“係統的分析在《紅樓夢》第七十五回裏生動地描述了賈府裏的一場擊鼓傳花遊戲:‘賈母坐下,左垂首賈赦,賈珍,賈璉,賈蓉,右垂首賈政,寶玉,賈環,賈蘭,團團圍坐。賈母便命折一枝桂花來,命一媳婦在屏後擊鼓傳花。若花到誰手中,飲酒一杯。於是先從賈母起,次賈赦,一一接過。鼓聲兩轉,恰恰在賈政手中住了,隻得飲了酒。’這場遊戲接著又把花傳到了寶玉和賈赦手裏,接著又傳到了在賈環手裏……如果用一個對象係統描述賈府,那麽賈母、賈赦、賈政、賈寶玉和賈環等等就應當分別由一個個具體類代表,而這場擊鼓傳花遊戲的類圖,按照責任鏈模式。”

大B:換言之,在擊鼓傳花遊戲裏麵,有下麵的幾種角色:抽象傳花者,或Handler角色、定義出參加遊戲的傳花人要遵守的規則,也就是一個處理請求的接口和對下家的引用;具體傳花者,或ConcreteHandler角色、每一個傳花者都知道下家是誰,要麽執行酒令,要麽把花向下傳。這個角色由賈母、賈赦、賈珍、賈璉、賈蓉、賈政、寶玉、賈環、賈蘭等扮演。擊鼓人,或Client角色、即行酒令的擊鼓之人。《紅樓夢》沒有給出此人的具體姓名,隻是說由一媳大B:可以看出,擊鼓傳花遊戲滿足責任鏈模式的定義,是純的責任鏈模式的例子。擊鼓傳花的類圖完全符合責任鏈模式的定義的類圖給出了這些類的具體接口設計。不難看出,DrumBeater(擊鼓者)、Player(傳花者)、JiaMu(賈母)、JiaShe(賈赦)、JiaZheng(賈政)、JiaBaoYu(寶玉)、JiaHuan(賈環)等組成這個係統。

下麵是客戶端類DrumBeater的源代碼:

publicclassDrumBeater

{

privatestaticPlayerplayer;

staticpublicvoidmain(String[]args)

{

player=newJiaMu(newJiaShe(newJiaZheng(newJiaBaoYu(newJiaHuan(null)))));

player.handle(4);

}

}

代碼清單1、DrumBeater的源代碼。

abstractclassPlayer

{

abstractpublicvoidhandle(inti);

privatePlayersuccessor;

publicPlayer(){successor=null;

}

protectedvoidsetSuccessor(PlayeraSuccessor)

{

successor=aSuccessor;

}

publicvoidnext(intindex)

{

if(successor!=null)

{

successor.handle(index);

}

else

{

System.out.println(“rogramterminated.”);

}

}

}

代碼清單2、抽象傳花者Play類的源代碼。

大B:“抽象類Player給出了兩個方法的實現,以格式setSuccessor(),另一個是next()。前者用來設置一個傳花者對象的下家,後者用來將酒令傳給下家。Player類給出了一個抽象方法handle(),代表執行酒令。”

下麵的這些具體傳花者類將給出handle()方法的實現。

classJiaMuextendsPlayer

{

publicJiaMu(PlayeraSuccessor)

{

this.setSuccessor(aSuccessor);

}

publicvoidhandle(inti)

{

if(i……1)

{

System.out.println(“JiaMugottadrink!”);

}

else

{

System.out.println(“JiaMupassed!”);next(i);

}

}

}

代碼清單3、代表賈母的JiaMu類的源代碼。

classJiaSheextendsPlayer

{

publicJiaShe(PlayeraSuccessor)

{

this.setSuccessor(aSuccessor);

}

publicvoidhandle(inti)

{

if(i……2)

{

System.out.println(“JiaShegottadrink!”);

}

else

{

System.out.println(“JiaShepassed!”);

next(i);

}

}

}

代碼清單4、代表賈赦的JiaShe類的源代碼。

classJiaZhengextendsPlayer

{

publicJiaZheng(PlayeraSuccessor)

{

this.setSuccessor(aSuccessor);

}

publicvoidhandle(inti)

{

if(i……3)

{

System.out.println(“JiaZhenggottadrink!”);

}

else

{

System.out.println(“JiaZhengpassed!”);

next(i);

}

}

}

代碼清單5、代表賈政的JiaZheng類的源代碼。

classJiaBaoYuextendsPlayer

{

publicJiaBaoYu(PlayeraSuccessor)

{

this.setSuccessor(aSuccessor);

}

publicvoidhandle(inti)

{

if(i……4)

{

System.out.println(“JiaBaoYugottadrink!”);

}

else

{

System.out.println(“JiaBaoYupassed!”);

next(i);

}

}

}

代碼清單6、代表賈寶玉的JiaBaoYu類的源代碼。

classJiaHuanextendsPlayer

{

publicJiaHuan(PlayeraSuccessor)

{

this.setSuccessor(aSuccessor);

}

publicvoidhandle(inti)

{

if(i……5)

{

System.out.println(“JiaHuangottadrink!”);

}

else

{

System.out.println(“JiaHuanpassed!”);

next(i);

}

}

}

代碼清單7、代表賈環的JiaHuan類的源代碼。

大B:“可以看出,DrumBeater設定了責任鏈的成員和他們的順序:責任鏈由賈母開始到賈環,周而複始。JiaMu類、JiaShe類、JiaZheng類、JiaBaoYu類與JiaHuan類均是抽象傳花者Player類的子類。實現的DrumBeater類在把請求傳給賈母時,實際上指定了由4號傳花者處理酒令。雖然DrumBeater並不知道哪一個傳花者類持有號碼4,但是這個號碼在本係統一開始就寫死的。這當然並不符合擊鼓傳花遊戲的精神,因為這個遊戲實際上要求有兩個同時進行的過程:擊鼓過程和傳花過程。擊鼓應當是定時停止的,當擊鼓停止時,執行酒令者就確定了。”