第七章 击鼓传花——责任型模式
7.1 击鼓传花
时间:12月23日 地点:大B房间 人物:大B,小A
大B:“你听过什么是击鼓传花吗?”
小A:“听过,是一种很热闹的游戏,具体是什么游戏我就不知道了。”
大B:“是的。击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声一落,假如花束在某人手中,则该人就得饮酒。假比说,贾母、贾赦、贾政、贾宝玉和贾环是五个参加击鼓传花游戏的传花者,他们组成一个环链。击鼓者将花传给贾母,开始传花游戏。花由贾母传给贾赦,由贾赦传给贾政,由贾政传给贾宝玉,又由贾宝玉传给贾环,由贾环传回给贾母,如此往复(见图7-1击鼓传花)。当鼓声停止时,手中有花的人就得执行酒令。”小A:“这样的啊?听起来很好玩喔。”
大B:“呵呵……是啊……”
图7-1 击鼓传花
7.2 责任型模式
大B:“击鼓传花便是责任链模式的应用。在责任链模式里,很多的对象由每一个对象对其下家的引用而联接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。”
小A:“哇,这就是责任链模式啊?听起来好像有点复杂喔。”
大B:“其实很简单的。责任链可能是一条直线、一个环链甚至一个树结构的一部分。”
小A:“这样一说好像很简单,但听起来好像很复杂似的。”
小A:“那你刚才说的红楼梦中击鼓传花的故事不是就是符合责任链模式吗?”
大B:“显然,击鼓传花符合责任链模式的定义。参加游戏的人是一个个的具体处理者对象,击鼓的人便是客户端对象。花代表酒令,是传向处理者的请求,每一个参加游戏的人在接到传来的花时,可选择的行为只有两个:一是将花向下传;一是执行酒令——喝酒。一个人不能既执行酒令,又向下家传花;当某一个人执行了酒令之后,游戏重新开始。击鼓的人并不知道最终是由哪一个做游戏的人执行酒令,当然执行酒令的人必然是做游戏的人们中的一个。”
击鼓传花的类图结构如图7-2击鼓传花系统的类图定义:
图7-2 击鼓传花系统的类图定义
大B:“单独考虑击鼓传花系统,那么像贾母、贾赦、贾政、贾宝玉和贾环等传花者均应当是‘具体传花者’的对象,而不应当是单独的类;但是责任链模式往往是建立在现有系统的基础之上的,因此链的结构和组成不由责任链模式本身决定。”
小A:“喔。是吗?”
大B:“系统的分析在《红楼梦》第七十五回里生动地描述了贾府里的一场击鼓传花游戏:‘贾母坐下,左垂首贾赦,贾珍,贾琏,贾蓉,右垂首贾政,宝玉,贾环,贾兰,团团围坐。贾母便命折一枝桂花来,命一媳妇在屏后击鼓传花。若花到谁手中,饮酒一杯。于是先从贾母起,次贾赦,一一接过。鼓声两转,恰恰在贾政手中住了,只得饮了酒。’这场游戏接着又把花传到了宝玉和贾赦手里,接着又传到了在贾环手里...如果用一个对象系统描述贾府,那么贾母、贾赦、贾政、贾宝玉和贾环等等就应当分别由一个个具体类代表,而这场击鼓传花游戏的类图,按照责任链模式。”
应当如图7-3红楼梦中的击鼓传花游戏的示意性类图所示:
图7-3 红楼梦中的击鼓传花游戏的示意性类图
大B:“换言之,在击鼓传花游戏里面,有下面的几种角色:抽象传花者,或Handler角色、定义出参加游戏的传花人要遵守的规则,也就是一个处理请求的接口和对下家的引用;具体传花者,或ConcreteHandler角色、每一个传花者都知道下家是谁,要么执行酒令,要么把花向下传。这个角色由贾母、贾赦、贾珍、贾琏、贾蓉、贾政、宝玉、贾环、贾兰等扮演。 击鼓人,或Client角色、即行酒令的击鼓之人。《红楼梦》没有给出此人的具体姓名,只是说由‘一媳妇’扮演。”
如图7-4贾府这次击鼓传花的示意性对象图:
图7-4 贾府这次击鼓传花的示意性对象图
大B:“可以看出,击鼓传花游戏满足责任链模式的定义,是纯的责任链模式的例子。图7-5击鼓传花的类图完全符合责任链模式的定义的类图给出了这些类的具体接口设计。不难看出,DrumBeater(击鼓者)、Player(传花者)、JiaMu(贾母)、JiaShe(贾赦)、JiaZheng(贾政)、JiaBaoYu(宝玉)、JiaHuan(贾环)等组成这个系统。”
图7-5 击鼓传花的类图完全符合责任链模式的定义
下面是客户端类DrumBeater的源代码:
代码清单1、DrumBeater的源代码。
代码清单2、抽象传花者Play类的源代码。
大B:“抽象类Player给出了两个方法的实现,以格式setSuccessor(),另一个是next()。前者用来设置一个传花者对象的下家,后者用来将酒令传给下家。Player类给出了一个抽象方法handle(),代表执行酒令。”
下面的这些具体传花者类将给出handle()方法的实现。
代码清单3、代表贾母的JiaMu类的源代码。
代码清单4、代表贾赦的JiaShe类的源代码。
代码清单5、代表贾政的JiaZheng类的源代码。
代码清单6、代表贾宝玉的JiaBaoYu类的源代码。
代码清单7、代表贾环的JiaHuan类的源代码。
大B:“可以看出,DrumBeater设定了责任链的成员和他们的顺序:责任链由贾母开始到贾环,周而复始。JiaMu类、JiaShe类、JiaZheng类、JiaBaoYu类与JiaHuan类均是抽象传花者Player类的子类。实现的DrumBeater类在把请求传给贾母时,实际上指定了由4号传花者处理酒令。虽然DrumBeater并不知道哪一个传花者类持有号码4,但是这个号码在本系统一开始就写死的。这当然并不符合击鼓传花游戏的精神,因为这个游戏实际上要求有两个同时进行的过程:击鼓过程和传花过程。击鼓应当是定时停止的,当击鼓停止时,执行酒令者就确定了。”(www.xing528.com)
7.3 责任型模式涉及到哪些角色
大B:“责任链模式是一种对象的行为模式。你知道他都涉及到哪些角色吗?
小A:“嘿嘿!不知道喔!”
大B:“没关系,我告诉你。所涉及到的角色如下:第一、抽象处理者角色、定义出一个处理请求的接口;假如需要,接口可以定义出一个方法,以返回对下家的引用。”
如图7-6抽象处理者角色给出了一个示意性的类图:
图7-6 抽象处理者角色
大B:“在图中的积累关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。第二、具体处理者(ConcreteHandler)角色、处理接到请求后,可以选择将请求处理掉,或者将请求传给下家。”
如图7-7具体处理者角色给出了一个示意性的类图。
图7-7 具体处理者角色
大B:“图7-7中的示意性的具体处理者ConcreteHandler类只有handleRequest()一个方法。”
责任链模式的静态类结构可见图7-8责任链模式的类图定义。
图7-8 责任链模式的类图定义
大B:“在图中还给出了一个客户端,以便可以更清楚地看到责任链模式是怎样应用的。你能写出抽象处理者的示意性源代码吗?”
小A:“写不出来喔!还请师兄教我。”
大B:“好。”
抽象处理者的示意性源代码:
代码清单8、抽象处理者的源代码。
具体处理者的示意性源代码:
代码清单9、具体处理者的源代码。
客户端的源代码如下:
代码清单10、客户端的源代码。
7.4 纯的与不纯的责任链模式
大B:“师弟,在责任链模式中还应该注意纯的与不纯的责任链模式。”
小A:“什么是纯的与不纯的责任链模式?”
大B:“一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,二是把责任推给下家。不答应出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接受;在一个不纯的责任链模式里面,一个请求可以最终不被任何接受端对象所接受。纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的;但是在实际的系统里,纯的责任链很难找到;假如坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大的意义了。”
小A:“这回我可明白了。”
7.6 什么情况下使用责任链模式
小A:“在什么情况下使用责任链模式?”
大B:“第一、系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出,第二、当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的。第三、当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求。第四、当处理一个请求的处理者对象集合需要动态地指定时。”
7.7 使用责任链模式的长处和短处
小A:“那使用责任链模式有哪些长处和短处?”
大B:“责任链模式减低了发出命令的对象和处理命令的对象之间的耦合,它允许多与一个的处理者对象根据自己的逻辑来决定哪一个处理者最终处理这个命令。换言之,发出命令的对象只是把命令传给链结构的起始者,而不需要知道到底是链上的哪一个节点处理了这个命令。显然,这意味着在处理命令上,允许系统有更多的灵活性。哪一个对象最终处理一个命令可以因为由那些对象参加责任链、以及这些对象在责任链上的位置不同而有所不同。”
7.8 责任链模式的实现
大B:“首先是链结构的由来值得指出的是,责任链模式并不创建出责任链。责任链的创建必须有系统的其它部分完成。责任链模式减低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。一个链可以是一条线,一个树,也可以是一个环。链的拓扑结构可以是单连通的或多连通的,责任链模式并不指定责任链的拓扑结构。但是责任链模式要求在同一个时间里,命令只可以被传给一个下家(或被处理掉);而不可以传给多于一个下家。”
在下面的责任链是系统已有的树结构的一部分,责任链是一个树结构的一部分。图中有阴影的对象给出了一个可能的命令传播路径。
图7-9 责任链是系统已有的树结构的一部分
大B:“责任链的成员往往是一个更大的结构的一部分。比如我们刚才说的《红楼梦》中击鼓传花的游戏中,所有的成员都是贾府的成员。如果责任链的成员不存在,那么为了使用责任链模式,就必须创建它们;责任链的具体处理者对象可以是同一个具体处理者类的实例。在Java的1.0版的AWT事件处理模型里,责任链便是视窗上的部件的容器等级结构。”
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。