锁定老帖子 主题:请问责任链真的是一种设计模式吗
精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-07-26
下面我详细说说我眼里的3种责任链模式: 1, 基本版责任链 这里所谓的基本责任链模式是指书上网上到处都有的责任链模式,其实这种模式网上到处都有,我就炒炒冷菜。让我们来看看下面这个关于它的uml图 图1,见附件 这是最简单的责任链模式,但是事实上我很少看到它在真实的系统中被使用(也有可能经验不足)。大家看到,我在图中给接口和类取的名字是Filter,没错,这个模式待会将会和正真的Filter相比较。下面我们就看看在spring中如何实现这么一种责任链模式。 首先是Filter的接口如下: /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com} * @version $Id$ */ public interface Filter { void executeFilter(); } 接着是一个抽象类,用来hold住下一个Filter实例的引用 /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com} * @version $Id$ */ public abstract class BaseFilter implements Filter { private Filter nextFilter; public void doNextFilter(){ if (nextFilter != null) { nextFilter.executeFilter(); } else { // do something you need here! System.out.println("there is no filter in the chain!!!!!!!!"); } } /** * 该方法用来注入下一个filter * @param nextFilter */ public void setNextFilter(Filter nextFilter) { this.nextFilter = nextFilter; } } 接着就是3个具体的filter了。同样,让我们来看一下这3个具体的filter的代码: 1权限检查filter /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com} * @version $Id$ */ public class AuthFilter extends BaseFilter { public void executeFilter(){ System.out.println("1------------check the user in this filter!"); doNextFilter(); System.out.println("2------------check the user in this filter!"); } } 2url重写filter /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com} * @version $Id$ */ public class URLRewriteFilter extends BaseFilter { public void executeFilter(){ System.out.println("1------------do url rewrite in this filter"); doNextFilter(); System.out.println("2------------do url rewrite in this filter"); } } 3其他的filter /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com} * @version $Id$ */ public class OtherFilter extends BaseFilter { public void executeFilter(){ System.out.println("1---------------do other things in this filter"); doNextFilter(); System.out.println("2---------------do other things in this filter"); } } 3个具体的filter有了之后就等组装“链”了。我们看看spring中的配置: <bean id="authFilter" class="org.easy.sample.designpattern.reschain.one.AuthFilter"> <property name="nextFilter" value="urlRewriteFilter"></property> </bean> <bean id="urlRewriteFilter" class="org.easy.sample.designpattern.reschain.one.URLRewriteFilter"> <property name="nextFilter" value="otherFilter"></property> </bean> <bean id="otherFilter" class="org.easy.sample.designpattern.reschain.one.OtherFilter"/> O了,就这样,当你从spring的的context中得到这个authFilter的时候,你其实得到了一条链,当你调用authFilter的方法是,这个调用会沿着链向下传递。到目前为止,我们已经实现了一个很搓(差)的责任链了,现在如果你在使用struts1.x系列,那么你就可以把这个代码搬到struts1.x里,给action实现它自己的不依赖于servlet容器的拦截器了(当然如果是你真的想的话)。 众所周知的责任链成型了,但是你能忍受这种所谓的责任链模式吗,至少我不能,即使我使用struts1.x,我也不会傻到把上面这个破代码搬到项目里去的。那么就再让我们来看看第二种责任链模式吧 2, 混血版责任链(基于观察者的责任链) 我之前写了一篇关于观察者的文章( http://www.iteye.com/topic/102068 ),在这篇文章中,我曾经说过观察者稍微改一下就可以变成责任链,也就是说我们可以通过观察者来实现同上面一样的功能。这里我只简单描述一下如何用观察者来实现责任链。当然首先我们必须要理解观察者模式,如果不清楚观察者模式的就看http://www.iteye.com/topic/102068 这篇文章吧。 如果说之前的那个责任链是通过nextFilter引用而形成的话,那么观察者模式中的观察者的集合也可以看作是一个链,只不过这个链上的每一个update方法都是要执行的,我们可以让它们某些执行某些不执行吗,当然可以了,只需要在写update方法的实现判断一下是否需要执行就行了,判断的条件就是update传进去的,比如说被观察者如下: /** * @author 张荣华(ahuaxuan) * @version $Id$ */ public class Subject extends Observable{ /** * 业务方法,一旦执行某个操作,则通知观察者 */ public void doBusiness(){ if (true) { super.setChanged(); } notifyObservers("mail"); notifyObservers("jms"); } public static void main(String [] args) { //创建一个被观察者 Subject subject = new Subject(); //创建两个观察者 Observer mailObserver = new MailObserver(); Observer jmsObserver = new JMSObserver(); //把两个观察者加到被观察者列表中 subject.addObserver(mailObserver); subject.addObserver(jmsObserver); //执行业务操作 subject.doBusiness(); } } 两个观察者如下: /** * @author 张荣华(ahuaxuan) * @version $Id$ */ public class JMSObserver implements Observer{ public void update(Observable o, Object arg) { if ("jms".equals(arg)) { System.out.println("发送消息给jms服务器的观察者已经被执行"); } } } 和 /** * @author 张荣华(ahuaxuan) * @version $Id$ */ public class MailObserver implements Observer{ /** * 这个类取名为MailObserver,顾名思义,她是一个用来发送邮件的观察者 */ public void update(Observable o, Object arg) { if ("mail".equals(arg)) { System.out.println("发送邮件的观察者已经被执行"); } } } 这样的话,每次通知观察者,在观察者列表中只有一个或多个观察者被执行了,如此一来,也实现了第一中观察者的功能。那么如果要为struts1.x实现不依赖于servlet容器的拦截器的时候我会用它吗?还是不会,因为虽然我也认为它是责任链,但事实上它更具备观察者的特征,比如说有一个列表,执行的时候需要遍历列表,并执行列表中对象的方法,这明显是观察者的特征。所以说它更像观察者,如果说观察者是父亲,责任链是母亲,那这个第二种责任链其实更像父亲。 虽然我还是不会用它来实现上面的需求,但是有一点我倒觉得这种用法比较像spring提供的事件机制,只不过在事件机制中是根据参数是否是属于某一类型(指instanceOf方法)来判断事件是否应该发生的,不管怎么说,都是用观察者模式来实现的(貌似我又在谈观察者的重要性了)。 其实上面的这个equals判断是可以用正则表达式来代替,也就是说每一个update方法执行之前可以先用正则表达式匹配一下参数是否符合要求,这样灵活性就更大了,在第3种责任链中,我将会用正则表达式来判断责任的传递。 接下来轮到第3种责任链了(口干舌燥,喝点水吧), 3, 潇洒版责任链 我们来看看所谓的潇洒版的责任链模式,如下图所示: 图2,见附件 很明显,这个图复杂多了,可以说这个责任链和基本版的责任链有本质的区别,他不是一个责任对象引用另外一个责任对象这种形式,通过这种形式形成了一条看不见的链。但是这够oo吗,所谓责任链,其实暗示着我们这里有两个类,一个是责任,一个是链,如果像基本版的责任链就只有一个责任类,通过责任之间的依赖形成了一个链。显然这种方式明显比不上潇洒版责任链,在这种责任链中,责任是一个对象,链也是一个对象。 我们来仔细看一下上面这个图,责任就是ConcreateHandler,链就是ConcreateHandlerChain,HandlerConfig是对责任的配置类,事实上链和责任之间的关系是靠HandlerConfig联系起来的。 首先我们来看一下链对象中的executeHandler()方法,这个方法是整个责任链的核心: private HandlerConfig [] handlerConfigs; private int firstElement = -1; private String doRegex; /** * 这个方法是核心方法,它根据正则表达式来决定哪些filter需要被执行,哪些filter不需要被执行 */ public void executeHandler() { if (handlerConfigs == null) { // 调用业务方法 return; } ++firstElement; if (firstElement < handlerConfigs.length) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this); } else { this.executeHandler(); } } }值得注意的是这个方法中有一个递规操作用,它的作用是当前handler不被执行的话就去判断是否执行下一个handler,我对递规还是有点怕的,大家有其他方法吗 我们再来看看责任对象中的executeHandler方法,这个方法就是每个责任对象的行为: /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com } * @version $Id$ */ public class AuthTestHandler implements Handler { public void executeHandler(HandlerChain handlerChain){ System.out.println("1----------check the user in this filter!"); handlerChain.executeHandler(); System.out.println("2----------check the user in this filter!"); } } 就是通过责任对象和链对象之间的重复调用,我们就实现了责任的传递,那为什么还需要一个config呢,这是因为责任链上的责任我认为应该是不带状态的,这个用户在写自己的责任对象的时候不需要考虑它的配置,因为通常它的配置是写在配置文件中的。我们来看一下config类: /** * @author 张荣华(ahuaxuan) * {@link http://ahuaxuan.iteye.com } * @version $Id$ */ public class HandlerConfig { private Handler handler; private String handlerName; private String regex; private Pattern pattern; //省略getter和setter方法 } 熟悉tomcat的同学大概已经猜出来了,这种责任链事实上就是tomcat中的filter,几乎一样的,那么让我们再看看测试方法: public static void main(String[] args) { HandlerConfig [] configs = new HandlerConfig[]{ new HandlerConfig(), new HandlerConfig(), new HandlerConfig() }; configs[0].setHandler(new AuthTestHandler()); configs[0].setHandlerName("authFilter"); configs[0].setRegex("\\d*"); configs[1].setHandlerName("charsetFilter"); configs[1].setHandler(new CharsetTestHandler()); configs[1].setRegex("^[A-Za-z]+$"); configs[2].setHandlerName("otherFilter"); configs[2].setHandler(new OtherTestHandler()); configs[2].setRegex("\\d*"); System.out.println("chain1"); EasyHandlerChain chain = new EasyHandlerChain("21"); chain.setHandlerConfigs(configs); chain.executeHandler(); System.out.println(""); System.out.println("chain2"); EasyHandlerChain chain1 = new EasyHandlerChain("asdf"); chain1.setHandlerConfigs(configs); chain1.executeHandler(); } 上面一些config对象的创建可以放到框架中,每个线程都创建一个EasyHandlerChain就行了,当然我们也可以把config对象创建所需要的配置放到annotation中去,但在这个例子中没有必要这样做。在测试方法中,每个config都设置了匹配表达式,每个chain对象都设置了匹配的值,简直和tomcat的filter配置一摸一样是吗。 附件是上面3种责任链的源代码, 尤其是第3种,只看上面贴出来的代码不太容易理解,看看源代码,运行一下就明白了。 总结一下吧:我们可以看到,在责任链的实现中我们使用到3种模式,这也是我的疑惑,责任链是一种设计模式吗,我认为它不应该属于某一种模式,责任链是一种思想,这种思想凌驾于模式之上,我们可以用模式来实现这种思想。这就是我想说的,如果你觉的这种理解是不正确的,请务必说明理由。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-07-26
实际上每一种设计模式都可以看作是一种思想.
除了几种特别简单的模式,实际运用中通常都是变体,或者是数种模式的组合. 职责链和观察者有几个重要区别 1.从概念上讲,职责链是个有序的线性表,所有的节点每次都按固定的顺序被调用.而观察者则没有这个要求. 2.职责链各个节点可以相互影响,即在事件的传递过程中,可以对事件进行修改,甚至阻止事件的传递.而观察者不行.观察者要求每次事件发生所有的观察者都应该收到通知,并且通知内容相同.各个观察者应该彼此独立,并不知道彼此的存在. 搂主的第二中实现就是观察者者模式了,没有任何职责链的味道。各个observer之间彼此独立监听事件,事件发生后独自进行操作。 楼主第三种实现属于观察者模式加入ChangeManager. 职责链典型的运用就是MFC在各个窗口中传递消息的方式了. |
|
返回顶楼 | |
发表时间:2007-07-27
一般第三种是比较常用的, 定义一个带执行顺序的队列(FIFO)的, 然后,置入handler.
handler可以在配置文件中进行描述. 然后由一个handler代理类进行处理. |
|
返回顶楼 | |
发表时间:2007-07-27
在职责链模式中,一个链中的各个节点是互相协作的关系。
最最常见的职责链模式就是虚函数了。 如果子类实现了该函数,那么就调用子类的,否则的话就到父类找。子类也可以做一部分处理后转发给基类。 继承链上的各个部分是互相协作的关系,耦合性比较强。 如果把毫无关系的两个东西硬塞进一个职责链中,就显得比较生硬了。这时候就应该按照观察者来理解,实现。 |
|
返回顶楼 | |
发表时间:2007-07-27
netpcc 写道 实际上每一种设计模式都可以看作是一种思想.
除了几种特别简单的模式,实际运用中通常都是变体,或者是数种模式的组合. 职责链和观察者有几个重要区别 1.从概念上讲,职责链是个有序的线性表,所有的节点每次都按固定的顺序被调用.而观察者则没有这个要求. 2.职责链各个节点可以相互影响,即在事件的传递过程中,可以对事件进行修改,甚至阻止事件的传递.而观察者不行.观察者要求每次事件发生所有的观察者都应该收到通知,并且通知内容相同.各个观察者应该彼此独立,并不知道彼此的存在. 搂主的第二中实现就是观察者者模式了,没有任何职责链的味道。各个observer之间彼此独立监听事件,事件发生后独自进行操作。 楼主第三种实现属于观察者模式加入ChangeManager. 职责链典型的运用就是MFC在各个窗口中传递消息的方式了. 你说的两点区别中对责任链的描述,我写的第3种都基本符合这个描述,为什么说他不是责任链了呢? 其实我说责任链不是设计模式我也有其他非正规理由,因为bob大叔那本敏捷的书里没有这个模式 阎宏那本书里是这样描述的: 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成的一条链。请求在这个链上传递,知道链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。 我觉得不是很准确 |
|
返回顶楼 | |
发表时间:2007-07-27
你的第三种责任链和tomcat的实现是一模一样的,
webwork2里的Interceptor也同样是这样做的。 |
|
返回顶楼 | |
发表时间:2007-07-27
嗯,思想不错。
不过就是有点不明白executeHandler() 为什么要用递归,一个循环遍历不就行了? |
|
返回顶楼 | |
发表时间:2007-07-27
第3种方式的各个Handle之间依旧是独立的。
虽然在实现上是依次调用的,但是Handle行为上应该说是不依赖这一点的。而且某一Handle也不可以截获或者修改事件。 阎宏的书我没看过,但是Gof原著上带ChangeManager的Observer模式,在结构上和第3种完全相同。 职责链的目的和Observer差异还是很大的。 除了前面的MFC和虚函数的例子,我再举2个职责链的例子。一个是IOStream。一个是开源项目Heritrix,里面大量运用职责链模式。这些都是不能用第3种实现来替代的。 如果LZ依旧认为第3种是职责链模式的话,请把任何一个例子试试改成第三种实现,看看能否保持行为特征不变。 |
|
返回顶楼 | |
发表时间:2007-07-27
netpcc 写道 阎宏的书我没看过,但是Gof原著上带ChangeManager的Observer模式,在结构上和第3种完全相同。 你是说tomcat的filter是观察者模式,而不是责任链模式对吗? netpcc 写道 职责链的目的和Observer差异还是很大的。 除了前面的MFC和虚函数的例子,我再举2个职责链的例子。一个是IOStream。一个是开源项目Heritrix,里面大量运用职责链模式。这些都是不能用第3种实现来替代的。 从这一点上我可以认定我们对责任链模式的理解有偏差,我相信你的理解应该是完全基于gof95的,但是我却不是,gof的书我没有看过,也许你能给出它的定义 netpcc 写道 如果LZ依旧认为第3种是职责链模式的话,请把任何一个例子试试改成第三种实现,看看能否保持行为特征不变。 虚函数和MFC是c++的东西吧,包括这个名词(虚函数)我都不知道应该怎么理解(c++我早还给老师了),很显然阎宏的书是用java语言的,你看他对责任链的描述和gof的描述是一样的吗? |
|
返回顶楼 | |
发表时间:2007-07-27
前面source没看仔细,向楼主道歉。
第三种方法里有个小的职责链模式。 即handlerConfigs里的handler不符合时,调用默认实现。 但是在handlerConfigs里的各个handler之间并没有形成职责链。 所以说实际的程序中通常会把各个模式组合在一起。 |
|
返回顶楼 | |