责任链(chain of responsibility)模式

来源:互联网 发布:linux定时关闭程序 编辑:程序博客网 时间:2024/05/01 12:42
   责任链可能是一条直线、一个环链甚至一个树结构的一部分。

  责任链模式的结构

  责任链模式是一种对象的行为模式,它所涉及到的角色如下:

  第一、抽象处理者(Handler)角色、定义出一个处理请求的接口;如果需要,接口可以定义出一个方法,以返回对下家的引用。下图给出了一个示意性的类图:


图2、抽象处理者角色。

  在图中的积累关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

  第二、具体处理者(ConcreteHandler)角色、处理接到请求后,可以选择将请求处理掉,或者将请 求传给下家。下图给出了一个示意性的类图。


图3、具体处理者角色。


  上图中的示意性的具体处理者ConcreteHandler类只有handleRequest()一个方法。

  责任链模式的静态类结构可见下图:  



图4、责任链模式的类图定义。

  在图中还给出了一个客户端,以便读者可以更清楚地看到责任链模式是怎样应用的。抽象处理者的示意性源代码:

public class Handler
 {
  public void handleRequest()
   {
    if (successor != null)
     {
       successor.handleRequest();
      }
     // Write your code here
    }
  public void setSuccessor(Handler successor)
    {
     this.successor = successor;
    }
  public Handler getSuccessor()
   {
    return successor;
   }
  private Handler successor;
  }  代码清单1、抽象处理者的源代码。

  具体处理者的示意性源代码:

public class ConcreteHandler extends Handler
  {
   public void handleRequest()
    {
     if (getSuccessor() != null)
     {
      getSuccessor().handleRequest();
     }
     if (successor != null)
     {
      successor.handleRequest();
     }
    // Write your code here
    }
  }  代码清单2、具体处理者的源代码。

  客户端的源代码如下:

public class Client
  {
   private Handler handler;
   public static void main(String[] args)
    {
     handler = new ConcreteHandler();
     //write your code here
    }
  }
  代码清单3、客户端的源代码。

  纯的与不纯的责任链模式

  一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,二是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。

  在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接受;在一个不纯的责任链模式里面,一个请求可以最终不被任何接受端对象所接受。

  纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的;但是在实际的系统里,纯的责任链很难找到;如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大的意义了。

  Java1.0版的AWT事件处理机制

  Java的1.0版中AWT库使用了责任链模式和命令模式来处理GUI的事件。由于视窗部件往往处在容器部件里面,因此当事件发生在一个部件上时,此部件的事件处理器可以处理此事件,然后决定是否将事件向上级容器部件传播;上级容器部件接到事件后可以在此处理此事件然后决定是否将事件再次向上级容器部件传播,如此往复,直到事件到达顶层部件。

  事件浮升机制

  比如,当一个视窗部件接到一个MOUSE_CLICKED事件时,事件首先传播到它所发生的部件上,然后向其容器部件传播。容器可以选择处理这个事件,或者再将此事件向更高一级的容器部件传播。事件如此一级级地向上传播,就像水底的气泡一点一点地冒到水面上一样,因此又叫做事件浮升(Event Bubbling)机制。下面就是一段典型的Java1.0版的AWT库里处理事件的代码:

public boolean action(Event event, Object obj)
  {
    if (event.target == btnOK)
     {
      doOKBtnAction();
     }
    else if (event.target == btnExit)
     {
      doExitBtnAction();
     }
    else
     {
      return super.action(event, obj);
     }
    return true;
  }
代码清单4、Java1.0版本中AWT处理事件的典型代码。

  在这段代码里面,action()判断目标部件是不是btnOK或btnExit;如果是,便运行相应的方法;如果不是,便返还true。一个方法返还true便使得事件停止浮升。

  AWT1.0的事件处理的模型的缺点之一

  AWT1.0的事件处理的模型是基于继承的。为了使一个程序能够捕捉GUI的事件并处理此事件,必须subclass此部件并且给其子类配备事件处理器,也就是置换掉action()方法或者handleEvent()方法。这不是应当提倡的做法:在一个面向对象的系统里,经常使用的应当是委派,继承不应当是常态。

  在一个复杂的GUI系统里,这样为所有有事件的部件提供子类,会导致很多的子类,这是不是很麻烦的吗?

  当然,由于事件浮升机制,可以在部件的树结构的根部部件里面处理所有的事件。但是这样一来,就需要使用复杂的条件转移语句在这个根部部件里辨别事件的起源和处理方法。这种非常过程化的处理方法很难维护,并且与面向对象的设计思想相违背。

  AWT1.0的事件处理的模型的缺点之二

  由于每一个事件都会沿着部件树结构向上传播,因此事件浮升机制会使得事件的处理变得较慢。这也是缺点之一。

  比如在有些操作系统中,鼠标每移动一个色素,都会激发一个MOUSE_MOVE事件。每一个这样的事件都会沿着部件的容器树结构向上传播,这会使得鼠标事件成灾。

  AWT1.0的事件处理的模型的缺点之三

  AWT1.0的事件处理的模型只适用于AWT部件类。这是此模型的另一个缺点。

  责任链模式要求链上所有的对象都继承自一个共同的父类,这个类便是java.awt.Component类。

  AWT1.0的事件处理的模型是不纯的责任链模式

  显然,由于每一级的部件在接到事件时,都可以处理此事件;而不论此事件是否在这一级得到处理,事件都可以停止向上传播或者继续向上传播。这是典型的不纯的责任链模式。

  AWT1.1以后的事件处理的模型

  自从AWT1.1以后,AWT的事件处理模型于1.0相比有了很大的变化。新的事件处理模型是建立在观察者模式的基础之上的,而不再是责任链模式的基础之上的。


二、关于过滤器的例子

      责任链模式(Chain of Responsibility)的目标是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

在处理用户的请求时可能要根据不同的情况对请求添加不同的处理逻辑,在这时候就可以利用责任链进行设计。当需要添加一个处理逻辑时可以很方便的添加一个处理的节点。

现在我们的需求是处理用户的请求,将用户提交的字符串信息进行层层处理,同时在处理完成之后返回结果时,也要对返回的字符串进行层层处理,而处理返回的情况时其处理的顺序和先前是正好相反的顺序。

首先建立用户的请求和接收对象Request和Response:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public class Request {  
  4.     String requestStr;  
  5.   
  6.     public String getRequestStr() {  
  7.         return requestStr;  
  8.     }  
  9.   
  10.     public void setRequestStr(String requestStr) {  
  11.         this.requestStr = requestStr;  
  12.     }  
  13.   
  14. }  

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public class Response {  
  4.     String responseStr;  
  5.   
  6.     public String getResponseStr() {  
  7.         return responseStr;  
  8.     }  
  9.   
  10.     public void setResponseStr(String responseStr) {  
  11.         this.responseStr = responseStr;  
  12.     }  
  13.   
  14. }  

我们将处理用户信息的逻辑抽象成为一个个的过滤器,进一步抽象出过滤器接口Filter:
[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public interface Filter {  
  4.     public void doFilter(Request request, Response response,FilterChain chain);  
  5.   
  6. }  

注意在Filte接口中doFilter方法参数中有FilterChain的一个变量,我们再建立FilterChain类:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. public class FilterChain implements Filter {  
  7.     List<Filter> filters = new ArrayList<Filter>();  
  8.     int index = 0;  
  9.   
  10.     public FilterChain addFilter(Filter f) {  
  11.         this.filters.add(f);  
  12.         return this;  
  13.     }  
  14.   
  15.     @Override  
  16.     public void doFilter(Request request, Response response, FilterChain chain) {  
  17.         if (index == filters.size())  
  18.             return;  
  19.         Filter f = filters.get(index);  
  20.         index++;  
  21.         f.doFilter(request, response, chain);  
  22.     }  
  23. }  

在FilterChain中继承了Filter接口,从而实现了doFilter方法,在FilterChain中又有一个index变量,该变量是用来标记当前访问的是哪一个过滤器,这些过滤器是存放在ArrayList中的,这样用户在使用的时候就可以实现自己的过滤器,编写自己的处理逻辑,从而将自己的过滤器添加到ArrayList中,再调用FilterChain的doFilter方法遍历整个责任链。

下面我们编写三个过滤器:

HTMLFilter类:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. /** 
  4.  * 过滤HTML中的脚本元素 
  5.  * @author lcq 
  6.  * 
  7.  */  
  8. public class HTMLFilter implements Filter {  
  9.   
  10.     @Override  
  11.     public void doFilter(Request request, Response response,FilterChain chain) {  
  12.         request.requestStr = request.getRequestStr().replace("<""[")  
  13.                 .replace(">""] --------HTMLFilter");  
  14.         chain.doFilter(request, response, chain);  
  15.         response.responseStr += "--------HTMLFilter";  
  16.           
  17.     }  
  18.   
  19. }  

SesitiveFilter类:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public class SesitiveFilter implements Filter {  
  4.   
  5.     @Override  
  6.     public void doFilter(Request request, Response response, FilterChain chain) {  
  7.         request.requestStr = request.getRequestStr().replace("敏感""  ")  
  8.                 .replace("猫猫""haha------SesitiveFilter");  
  9.         chain.doFilter(request, response, chain);  
  10.         response.responseStr += "------SesitiveFilter";  
  11.   
  12.     }  
  13.   
  14. }  

FaceFilter类:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public class FaceFilter implements Filter {  
  4.   
  5.     @Override  
  6.     public void doFilter(Request request, Response response, FilterChain chain) {  
  7.         request.requestStr = request.getRequestStr().replace(":)",  
  8.                 "^V^-------FaceFilter");  
  9.         chain.doFilter(request, response, chain);  
  10.         response.responseStr += "-------FaceFilter";  
  11.   
  12.     }  
  13.   
  14. }  

最后编写测试类:

[java] view plaincopy
  1. package com.lcq.filter;  
  2.   
  3. public class Main {  
  4.     public static void main(String[] args) {  
  5.         String message = "敏感词汇,重庆,<script> 躲猫猫 :)";  
  6.         Request request = new Request();  
  7.         request.setRequestStr(message);  
  8.         Response response = new Response();  
  9.         response.setResponseStr("response");  
  10.         FilterChain fc = new FilterChain();  
  11.         fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter());  
  12.   
  13.         FilterChain fc2 = new FilterChain();  
  14.         fc2.addFilter(new FaceFilter());  
  15.         fc.addFilter(fc2);  
  16.         fc.doFilter(request, response,fc);  
  17.         System.out.println("request = " + request.getRequestStr());  
  18.         System.out.println("response = " + response.getResponseStr());  
  19.     }  
  20.   
  21. }  
在上面的实例中应该注意两个地方:

1.我们建立的FilterChain中继承了Filter接口,所以在测试类中就可以像使用其他的过滤器一样使用FilterChain,大大提高了灵活性;

2.对于实现责任链的访问处理顺序问题,该问题的解决使用的是递归的思想,从而使先调用的结点在处理返回结果时其调用过滤器的顺序是相反的。这种解决方案在Struts和其他框架中实现过滤器和拦截器使用的较为普遍,并且十分巧妙。

ref:http://lilai.iteye.com/blog/1607030

    http://blog.csdn.net/liuchangqing123/article/details/7386912

原创粉丝点击