大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,但是这个号码在本系统一开始就写死的。这当然并不符合击鼓传花游戏的精神,因为这个游戏实际上要求有两个同时进行的过程:击鼓过程和传花过程。击鼓应当是定时停止的,当击鼓停止时,执行酒令者就确定了。”