设计模式之职责链(Chain of Responsibility)
现在还谈基本的设计模式,似乎很对不起大家,但是,如果你如果像我一样,得不断地向别人解释同一样东西,直到口水都干了的时候,你就会能够体会我的心境。
现在的设计模式的书并不是很多(.net的),很多一部分人并不知道去java的书籍学习一些编程上的思想,所以,只有写写系列文章,以后谁再问我时,我就说,去cnblog上面看去,多快乐
我不想用很模糊的语言来描述它,只想用最一般的话来修正它,如果各位朋友能够指出我理解上的问题,当然更好,这个系列的文章我打算多写点,让一些初者减少迷茫。
首先,先看看设计模式手册中的描述吧。
这个图,一开始看,确实让人有点晕乎晕乎的(特指不熟悉uml图),参照文字说明及图的表示,可以明白,handler实际上表现出了一个自我递归的关系。
在设计模式中的职责链的意图是这样描述的:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.
这句话的意思,就是指,你发送一个消息给一个对象,当这个对象发现这个消息不是自己能够处理或是自己想要的消息的时候,就把它转发给其它对象,直到有对象处理这个消息时就中止。
一般说来,实现职责链的模式,有几个要点:
1、每个对象中的代码,能够指出它要转发的对象,并能够说明自己是不是链中的最后一个对象,但这个指定不是由对象本身来自己指定,而是给出一个接口,由外部的实体来指定。
2、有专门的类中,将各个对象链接起来。
3、有发送信息的入口。
在<<C#设计模式>>一书中,有一个例子就是,窗体上有几个控件,当你按下F1键时,消息会在控件间游走一遍,然后由控件自己来判断响不响应消息。
我认为,这个例子不是很直观,而且可以算得上是不良的设计,或者说是把设计模式滥用了。
试想一下,如果你在窗体上删除一个控件,你将不得不在链接各个对象的类中,把相关代码删除掉。如果是添加一个控件,则你必须得在添加的控件中加入能够指明它是否还有转发对象,是否是链中的最后一个对象的代码。不仅如此,如果控件量比较大,要写出的多出来的代码可不是开玩笑的。相比之下,你每添加一个控件,然后就针对它的keypress事件添加代码,不但简单,而且高效(消息不必循环一个链),添加与删除也容易得多。
高效、易扩展,这才是设计模式能够被广泛使用的本质,要理解清楚该模式的真正作用,然后才能很好的使用。
根据职责链的适用性,我们可以得出如下的理解。
职责链模式的使用,一般针对于此类情况:
1、发送一条消息,但你只要求对象处理消息是有次序的,并且只要有对象处理这个消息时就中止。
2、发送一条消息,你并不知道究竟有一个还是多个对象需要处理该消息。这时,你就可以设计一个入口点,让这些不确定哪些才会响应消息的对象形成一个链条,然后让它们自己来判断是否响应消息。
而且上述的内容不应是固定不变,在程序中,你可以自由而灵活地指定,比如,你可以指定各个对象响应消息的先后次序,这些都是动态的。
“动态的”,请注意一下这个词语,这是职责链中最灵活的部分了,它能够发挥的功效也在这里体现。
职责链可以用于这类场景:
当你制定了一个业务流程,然后你的程序都是按此业务流程所办理,但需求突然变更了,客户说它调整了处理流程(修改了资料或添加了新的流程),要求程序要相应的更改。此时,如果你对业务流程中的消息传递是采用职责链来完成的,你的事情就好办了,只须调整一下处理对象的次序或增加一个对象,然后只需修改极少量,甚至不用修改其它的代码就可以方便地满足用户的需要。
举一个实际生活中的处理的例子:
学校里的学生,在进校时,到财务部先盖章,然后到后勤部盖,这个流程表示先交钱,再领东西。而毕业时,反了过来,先要到后勤部盖,再到财务部盖,表示的是退了东西,然后可以去领退的钱。
如果采用一般的处理方式,因为是两个对象,往往会考虑采用一个bool变量来表示另一个部门的章是否已盖,但当你的程序做好了后,如果学校突然告诉你,它们的流程改变,中间还要到学生处盖个章,那么,就会出极大的问题。
这种情况下,采用了职责链的话,因为在职责链中,每一个对象都需要指明它的后面是否还有链接,所以,这个流程无论是添加还是删除了中间的处理过程,都不会影响到你总体的设计。
实践的办法就是,为每一个部门建立bool值,表示章盖了没有。
虽然取决于先后的关系是章盖了没有,但现在,你可以让其中一个对象响应消息,然后在应该盖章的地方作上已盖的标记,如果不产生已盖标识的情况下,就中止消息的处理和传递,这样,每个对象只需处理到来的消息,而无须关系其它对象的状态,可以让对象之间的耦合度降低。
也许某些朋友会问,你以上的处理方法(中止消息的处理和传递)似乎与“将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。”这句话的原则有些不同,原文是指这条链中没有消息响应则转发,好像有些不一样。
实际上,在上面所述的链条中,每一条消息都被处理了,因为上面所述链条比较简单,中间没有穿插其它的描述,为了方便理解,我们设想如下场景:
后勤处->学生处->财务处,对于一些聪明的学校,为了达到学生在各处注册的最高效,于是便在中间针对某些班级插入了“学生处”的盖章,从而减轻一部分从“后勤处”到“财务处”的压力,很不幸地是,你就遇见了这种聪明的学校,所以你必须处理它。
这里只有财务处才管后勤处盖没盖章,中间的学生处却并不关心你在其它地方盖章没有,因为它只管理你的档案注册(某些学校不一样的是,要求你先办完所有手续,然后才到在学生处报道,同样的,也可以参考类似的办法来处理)。实际上的整个流程处理的核心消息是盖章这一点,这里屏掉了每个对象对其它对象的状态检测,而只是采用了简单的,不干这个就不能干那个方法来处理。这样,问题就简单了许多,尤其是针对复杂的业务操作的时候,采用职责链,可以在保护各个对象间的关系不变的情况,自由地变换处理流程。
现在回过头来看,很明显,上述方法,基本可以满足我们的需求,因为对象之间不发生干预关系,你可以自己地调整流程。
但是,上述结构也有一些不良之处,因为它的适应性还不够,上述方法只能适合于某一个学校的状况,但并不适合所有学校的情况,根源是在于,我们使用职责链模式时,实际采用的是类似于数据结构中的链表结构。
对于更复杂的需求,在进行对象间的链接的时候,可以考虑采用更复杂的结构描述,比如说,树形结构,这样,就可以充分利用已有的知识,在进行对象间的访问策略时,可以充分利用树的特性,从而达到已有的知识利用的目的,比如:可以利用高效的递归,简化问题的处理。
采用树形结构时,可以考虑,采用只有一个入口点的方式,针对有分支的地方,采用专门的决策对象进行处理,这时就需要结合到其它的模式,一般说来,链的处理,考虑时,采用越简单的建模越能有效地提高可维护性,降低对象维护的难度。
实际上,设计模式中的任何一种模式,对于一个经常搞开发的人来说,已经自动地使用了其中一种或多种模式,但也有人可能熟悉其中的一到两种模式,对于这类人来说,熟悉一下设计模式,对于提高编程开发的效率和软件成品的质量,是十分有帮助作用的。
注:上述仅是个人理解,如有不妥之处,请指出。