首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

请教责任链真的是一种设计模式吗

2012-10-29 
请问责任链真的是一种设计模式吗坛子上讨论设计模式的也挺多的,但是关于这个责任链模式还没有人提及,我对

请问责任链真的是一种设计模式吗
坛子上讨论设计模式的也挺多的,但是关于这个责任链模式还没有人提及,我对责任链模式也有些疑惑,我的疑惑是,责任链真的是一种设计模式吗,我为什么有这种疑惑呢,因为在我用到和学习的责任链一共有3种,但是n多的书上就是那么一种最基础的责任链模式。在我的心里,只要能完成责任传递的都可以称之为责任链。当然也有可能我的这种理解是错误的,但如果我的理解是错误的,那么就是说每种模式都是有固定形态的,简单的模式是有固定形态,这我承认,但是如果说所有的模式都有固定的形态我不太相信,下面我就用文章来说明我的观点。

下面我详细说说我眼里的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" value="urlRewriteFilter"></property>    </bean>    <bean id="urlRewriteFilter" value="otherFilter"></property>    </bean><bean id="otherFilter" name="code">/** * @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种模式,这也是我的疑惑,责任链是一种设计模式吗,我认为它不应该属于某一种模式,责任链是一种思想,这种思想凌驾于模式之上,我们可以用模式来实现这种思想。这就是我想说的,如果你觉的这种理解是不正确的,请务必说明理由。
if (firstElement < handlerConfigs.length) {               if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) {                   [b]handlerConfigs[firstElement].getHandler().executeHandler(this);   [/b]            } else {                   this.executeHandler();               }           }   
和ConcreateHandler中的
    public void executeHandler(HandlerChain handlerChain){           System.out.println("1----------check the user in this filter!");           [b]handlerChain.executeHandler();   [/b]        System.out.println("2----------check the user in this filter!");       } 

互相交叉,有违单向依赖法则

另,是不是可以作下面重构,不知有没有影响代码的意图。将
        ++firstElement;           if (firstElement < handlerConfigs.length) {               if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) {                   handlerConfigs[firstElement].getHandler().executeHandler(this);               } else {                   this.executeHandler();               }           }
  
改写成
for (firstElement = 0; firstElement < handlerConfigs.length; firstElement++) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this);return;}   }



if (firstElement < handlerConfigs.length) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { [b]handlerConfigs[firstElement].getHandler().executeHandler(this); [/b] } else { this.executeHandler(); } }
和ConcreateHandler中的
    public void executeHandler(HandlerChain handlerChain){           System.out.println("1----------check the user in this filter!");           [b]handlerChain.executeHandler();   [/b]        System.out.println("2----------check the user in this filter!");       } 

互相交叉,有违单向依赖法则

单向依赖法则是指什么呢,好像面向对象设计的原则里好像没有关于单向依赖的描述呀。
++firstElement; if (firstElement < handlerConfigs.length) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this); } else { this.executeHandler(); } }  
改写成
for (firstElement = 0; firstElement < handlerConfigs.length; firstElement++) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this);return;}   }




如果这样重构,导致的结果就是只要链上一某个不符合,那调用就结束(所有的方法依次出栈),这并不是我的目标,我是想如果有一个责任不符合,那么就继续执行链上的下一个责任,直到链尾为止,到达链尾后所有的方法依次出栈。 ++firstElement; if (firstElement < handlerConfigs.length) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this); } else { this.executeHandler(); } }  
改写成
for (firstElement = 0; firstElement < handlerConfigs.length; firstElement++) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this);return;}   }




如果这样重构,导致的结果就是只要链上一某个不符合,那调用就结束(所有的方法依次出栈),这并不是我的目标,我是想如果有一个责任不符合,那么就继续执行链上的下一个责任,直到链尾为止,到达链尾后所有的方法依次出栈。
不好意思,上面的代码我没有实际的编译一下,其实你想要的效果就是HandlerChain的executeHandler方法,会调用一个符合条件的Handler对象进行处理,如果下标firstElement处的Handler不符合规则,就探索下一个,直至遍历到一个符合的为止(如果存在的话)。
所以上面的重构代码,只需把firstElement 放到上面即可,如下所示,已通过测试,和你的效果一样
    firstElement += 1;    for (; firstElement < handlerConfigs.length ; firstElement++) {      if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) {        handlerConfigs[firstElement].getHandler().executeHandler(this);        return;      }    }


单向依赖法则是我从无环依赖原则(ADP)推演的,ADP表达包和包之间不能存在相互依赖,包是一种实体,而类也是一种实体,只不过小于包实体,所以类之间虽然在特殊的情况下不能避免循环依赖,但只要可能最好还是避免,这样得到的设计会更简单、更清晰和容易理解

firstElement += 1; for (; firstElement < handlerConfigs.length ; firstElement++) { if (handlerConfigs[firstElement].getPattern().matcher(doRegex).matches()) { handlerConfigs[firstElement].getHandler().executeHandler(this); return; } }

这个改法不错,之前有个哥们说到了,我之前没有认真得去想他的话,他说:

solospider 写道嗯,思想不错。
不过就是有点不明白executeHandler() 为什么要用递归,一个循环遍历不就行了?
递规是我自然而然的去想到的,想到之后我就进了套子,认为这里只有递规了,也没有多想了,呵呵,谢谢,还是这个遍历比较好,其实俺也比较讨厌递规,出了问题不好找。

qinysong 写道
单向依赖法则是我从无环依赖原则(ADP)推演的,ADP表达包和包之间不能存在相互依赖,包是一种实体,而类也是一种实体,只不过小于包实体,所以类之间虽然在特殊的情况下不能避免循环依赖,但只要可能最好还是避免,这样得到的设计会更简单、更清晰和容易理解

首先,您能从ADP中推演单向依赖法则证明您对它的理解非常深入,这让我钦佩,但是我却不能同意包是一种实体的说法(抑或我对这个实体的定义的理解有偏差),我倒是觉得类之间的循环依赖并没有什么不妥。之所以这样说是因为到处都有这种情况,比说tomcat,webwork,等

netpcc 写道
我还特意去翻了一下GOF的原著,里面2个模式的典型情况在结构上确实是相同的。
只不过职责连的变种比较多罢了。

就是因为责任链的变种多,就让我觉得他不应该是一种模式,呵呵(有点偏执了),查了一下headfirstdesignpattern,这本书里貌似也没有责任链这个模式的描述。

24 楼 dykl001 2007-08-09   模式本来就是一种思想,我觉得,不仅为解决某类问题的一种方案,更是一种思想的体现。面向oo的编程方式是在接触了模式之后,当然了只是我个人感受 25 楼 cljspn 2008-04-25   挺好呀,真的受益了,我现在也正在研究设计模式呀,想与楼主交个朋友,不知可不可以,我的Msn为cljspn@hotmail.com如果可以就加我。不想就算了哈哈 26 楼 jbon 2008-05-20   任何递归都是可以改写成循环 27 楼 jbon 2008-05-20   我认为这更像一种框架,我认为

模式,是一种思想,是不能以具体的编码代替各种具体情况的,不能简单的移植代码使用的
框架,是一种产品,是可以以具体的编码代替各种具体情况的,是可以将代码移植并使用的

比如,楼主的第三种方式,代码是可以直接拿到别的地方使用的,只需具体实现Handler就可以了。
28 楼 taowen 2008-06-30   用第一种方式实现的职责链可以在runtime选择下一个handler是谁,但是用第二种就不能,用第三种就需要把这个放在配置里.灵活性,还是第一种最好.

热点排行