显然,击鼓传花符合责任链模式的定义。参加游戏的人是一个个的具体处理者对象,击鼓的人便是客户端对象。花代表酒令,是传向处理者的请求,每一个参加游戏的人在接到传来的花时,可选择的行为只有两个:一是将花向下传;一是执行酒令---喝酒。一个人不能既执行酒令,又向下家传花;当某一个人执行了酒令之后,游戏重新开始。击鼓的人并不知道最终是由哪一个做游戏的人执行酒令,当然执行酒令的人必然是做游戏的人们中的一个。
击鼓传花的类图结构如下:
图5、击鼓传花系统的类图定义。
单独考虑击鼓传花系统,那么像贾母、贾赦、贾政、贾宝玉和贾环等传花者均应当是“具体传花者”的对象,而不应当是单独的类;但是责任链模式往往是建立在现有系统的基础之上的,因此链的结构和组成不由责任链模式本身决定。
系统的分析
在《红楼梦》第七十五回里生动地描述了贾府里的一场击鼓传花游戏:“贾母坐下,左垂首贾赦,贾珍,贾琏,贾蓉,右垂首贾政,宝玉,贾环,贾兰,团团围坐。...贾母便命折一枝桂花来,命一媳妇在屏后击鼓传花。若花到谁手中,饮酒一杯...于是先从贾母起,次贾赦,一一接过。鼓声两转,恰恰在贾政手中住了,只得饮了酒。”这场游戏接着又把花传到了宝玉和贾赦手里,接着又传到了在贾环手里...
如果用一个对象系统描述贾府,那么贾母、贾赦、贾政、贾宝玉和贾环等等就应当分别由一个个具体类代表,而这场击鼓传花游戏的类图,按照责任链模式,应当如下图所示:
图6、红楼梦中的击鼓传花游戏的示意性类图。
换言之,在击鼓传花游戏里面,有下面的几种角色:
抽象传花者,或Handler角色、定义出参加游戏的传花人要遵守的规则,也就是一个处理请求的接口 和对下家的引用;
具体传花者,或ConcreteHandler角色、每一个传花者都知道下家是谁,要么执行酒令,要么把花 向下传。这个角色由贾母、贾赦、贾珍、贾琏、贾蓉、贾政、宝玉、贾环、贾兰等扮演。 击鼓人,或Client角色、即行酒令的击鼓之人。《红楼梦》没有给出此人的具体姓名,只是说由“一 媳妇”扮演。
图7、贾府这次击鼓传花的示意性对象图。
可以看出,击鼓传花游戏满足责任链模式的定义,是纯的责任链模式的例子.
Java系统的解下面的类图给出了这些类的具体接口设计。读者不难看出,DrumBeater(击鼓者)、Player(传花者)、JiaMu(贾母)、JiaShe(贾赦)、JiaZheng(贾政)、JiaBaoYu(宝玉)、JiaHuan(贾环)等组成这个系统。
图8、击鼓传花的类图完全符合责任链模式的定义。
下面是客户端类DrumBeater的源代码:
//DrumBeater的源代码
public class DrumBeater
{
private static Player player;
static public void main(String[] args)
{
player = new JiaMu( new JiaShe( new JiaZheng( new JiaBaoYu(new JiaHuan(null)))));
player.handle(4);
}
}
//抽象传花者Play类的源代码
abstract class Player
{
abstract public void handle(int i);
private Player successor;
public Player() { successor = null;}
protected void setSuccessor(Player aSuccessor)
{
successor = aSuccessor;
}
public void next(int index)
{
if( successor != null )
{
successor.handle(index);
}
else
{
System.out.println("Program terminated.");
}
}
}
}
抽象类Player给出了两个方法的实现,以格式setSuccessor(),另一个是next()。前者用来设置一个传花者对象的下家,后者用来将酒令传给下家。Player类给出了一个抽象方法handle(),代表执行酒令。
下面的这些具体传花者类将给出handle()方法的实现。
//贾母的JiaMu类
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);
}
}
}
//贾赦的JiaShe类
class JiaShe extends Player
{
public JiaShe(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 2 )
{
System.out.println("Jia She gotta drink!");
}
else
{
System.out.println("Jia She passed!");
next(i);
}
}
}
//贾政的JiaZheng类
class JiaZheng extends Player
{
public JiaZheng(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 3 )
{
System.out.println("Jia Zheng gotta drink!");
}
else
{
System.out.println("Jia Zheng passed!");
next(i);
}
}
}
//贾宝玉的JiaBaoYu类
class JiaBaoYu extends Player
{
public JiaBaoYu(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 4 )
{
System.out.println("Jia Bao Yu gotta drink!");
}
else
{
System.out.println("Jia Bao Yu passed!");
next(i);
}
}
}
//JiaHuan类
class JiaHuan extends Player
{
public JiaHuan(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 5 )
{
System.out.println("Jia Huan gotta drink!");
}
else
{
System.out.println("Jia Huan passed!");
next(i);
}
}
}
可以看出,DrumBeater设定了责任链的成员和他们的顺序:责任链由贾母开始到贾环,周而复始。JiaMu类、JiaShe类、JiaZheng类、JiaBaoYu类与JiaHuan类均是抽象传花者Player类的子类。
本节所实现的DrumBeater类在把请求传给贾母时,实际上指定了由4号传花者处理酒令。虽然DrumBeater并不知道哪一个传花者类持有号码4,但是这个号码在本系统一开始就写死的。这当然并不符合击鼓传花游戏的精神,因为这个游戏实际上要求有两个同时进行的过程:击鼓过程和传花过程。击鼓应当是定时停止的,当击鼓停止时,执行酒令者就确定了。但是本节这样做可以使问题得到简化并将读者的精力放在责任链模式上,而不是两个过程的处理上。
在什么情况下使用责任链模式
在下面的情况下使用责任链模式:
第一、系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出,
第一、当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的。
第二、当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求。
第三、当处理一个请求的处理者对象集合需要动态地指定时。
使用责任链模式的长处和短处
责任链模式减低了发出命令的对象和处理命令的对象之间的耦合,它允许多与一个的处理者对象根据自己的逻辑来决定哪一个处理者最终处理这个命令。换言之,发出命令的对象只是把命令传给链结构的起始者,而不需要知道到底是链上的哪一个节点处理了这个命令。
显然,这意味着在处理命令上,允许系统有更多的灵活性。哪一个对象最终处理一个命令可以因为由那些对象参加责任链、以及这些对象在责任链上的位置不同而有所不同。
责任链模式的实现
链结构的由来
值得指出的是,责任链模式并不创建出责任链。责任链的创建必须有系统的其它部分完成。
责任链模式减低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。一个链可以是一条线,一个树,也可以是一个环。链的拓扑结构可以是单连通的或多连通的,责任链模式并不指定责任链的拓扑结构。但是责任链模式要求在同一个时间里,命令只可以被传给一个下家(或被处理掉);而不可以传给多于一个下家。在下面的图中,责任链是一个树结构的一部分。
图9、责任链是系统已有的树结构的一部分。图中有阴影的对象给出了一个可能的命令传播路径。
责任链的成员往往是一个更大的结构的一部分。比如在前面所讨论的《红楼梦》中击鼓传花的游戏中,所有的成员都是贾府的成员。如果责任链的成员不存在,那么为了使用责任链模式,就必须创建它们;责任链的具体处理者对象可以是同一个具体处理者类的实例。