策略模式和责任链模式(5)

来看客户端调用代码:


public class Test {
    public static void main(String[] args) {
        MemberService memberService = new MemberService();
        memberService.login("tom","666");
    }
}


其运行结果如下:


用户名和密码校验成功,可以往下执行
登录成功!
您是管理员,允许操作


其实我们平时使用的很多权限校验框架都是运用这样一个原理,将各个维度的权限处理解耦之后再

串联起来,各自只处理各自相关的职责。如果职责与自己不相关则抛给链上的下一个Handler, 俗称踢

皮球。


责任链模式和建造者模式结合使用


因为责任链模式具备链式结构,而上面代码中,我们看到,负责组装链式结构的角色是

MemberService ,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代


码相对臃肿,且后续更改处理者或消息类型时,都必须在MemberService中进行修改,不符合开闭原 则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会


想到建造者模式,使用建造者模式,我们完全可以MemberService指定的处理节点对象进行自动链式组装,客户只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不


同 ,构造出来的链式结构也随之不同。我们来改造一下,修改Handler的代码:


public abstract class Handler<T> {
    protected Handler next;
    public void next(Handler next){ this.next = next;}

    public abstract void doHandler(Member member);

    public static class Builder<T>{
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
           // do {
                if (this.head == null) {
                    this.head = this.tail = handler;
                    return this;
                }
                this.tail.next(handler);
                this.tail = handler;
           // }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部
            return this;
        }

        public Handler<T> build(){
            return this.head;
        }
    }
}


然 后 ,修改MemberService的代码:


public class MemberService {
    public void login(String loginName,String loginPass){
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
               .addHandler(new LoginHandler())
               .addHandler(new AuthHandler());
        builder.build().doHandler(new Member(loginName,loginPass));
        //用过Netty的人,肯定见过
    }
}


因为建造者模式要构建的是节点处理者,因此我们把Builder作为Handler的静态内部类,并且因 为客户端无需进行链式组装,因此我们还可以把链式组装方法next。方法设置为private,使 Handler

更加高内聚,代码如下:


public abstract class Handler<T> {
    protected Handler chain;

    private void next(Handler handler) {
        this.chain = handler;
    }
}


通过这个案例,小伙伴们应该已经感受到责任链和建造者结合的精 ITo


七、责任链模式在源码中的体现


首先我们来看责任链模式在JDK中的应用,来看一个J2EE标准中非常常见的Filter类:


public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    public void destroy();
}


这个Filter接口的定义非常简单,相当于责任链模型中的Handler抽象角色,那么它是如何形成一 条责任链的呢?我来看另外一个类,其实在doFilte()方法的最后一个参数我们已经看到其类型是

FilterChain类 ,来看它的源码:


public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}


FilterChain类中也只定义了一个doFilter()方法,那么它们是怎么串联成一个责任链的呢?实际上 J2EE只是定义了一个规范,具体处理逻辑是由使用者自己来实现。我们来看一个Spring的实现

MockFilterChain类:


public class MockFilterChain implements FilterChain {

   @Nullable
   private ServletRequest request;

   @Nullable
   private ServletResponse response;

   private final List<Filter> filters;

   @Nullable
   private Iterator<Filter> iterator;

   public MockFilterChain() {
      this.filters = Collections.emptyList();
   }

   public MockFilterChain(Servlet servlet) {
      this.filters = initFilterList(servlet);
   }

   public MockFilterChain(Servlet servlet, Filter... filters) {
      Assert.notNull(filters, "filters cannot be null");
      Assert.noNullElements(filters, "filters cannot contain null values");
      this.filters = initFilterList(servlet, filters);
   }

   private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
      Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
      return Arrays.asList(allFilters);
   }

   @Nullable
   public ServletRequest getRequest() {
      return this.request;
   }

   @Nullable
   public ServletResponse getResponse() {
      return this.response;
   }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
      Assert.notNull(request, "Request must not be null");
      Assert.notNull(response, "Response must not be null");
      Assert.state(this.request == null, "This FilterChain has already been called!");

      if (this.iterator == null) {
         this.iterator = this.filters.iterator();
      }

      if (this.iterator.hasNext()) {
         Filter nextFilter = this.iterator.next();
         nextFilter.doFilter(request, response, this);
      }

      this.request = request;
      this.response = response;
   }

   public void reset() {
      this.request = null;
      this.response = null;
      this.iterator = null;
   }

   private static class ServletFilterProxy implements Filter {
      private final Servlet delegateServlet;

      private ServletFilterProxy(Servlet servlet) {
         Assert.notNull(servlet, "servlet cannot be null");
         this.delegateServlet = servlet;
      }

      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         this.delegateServlet.service(request, response);
      }

      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
      }

      @Override
      public void destroy() {
      }

      @Override
      public String toString() {
         return this.delegateServlet.toString();
      }
   }
}


它把链条中的所有Filter放到List中 ,然后在调用doFilter。方法时循环迭代List , 也就是说List

中的Filter会顺序执行。


再来看一个在Netty中非常经典的串行化处理Pipeline就采用了责任链设计模式。它底层采用双向 链表的数据结构,将链上的各个处理器串联起来。客户端每一个请求的到来,Netty都认为Pipeline中 的所有的处理器都有机会处理它。因此,对于入栈的请求全部从头节点开始往后传播,一直传播到尾节

点才会把消息释放掉。来看一个Netty的责任处理器接口 ChannelHandler:


public interface ChannelHandler {
    // 当 handler被添加到真实的上下文中,并且准备处理事件时被调用
    // handler被添加进去的回调 
    void handlerAdded(ChannelHandlerContext var1) throws Exception;

    // 是 handler被移出的后的回调
    void handlerRemoved(ChannelHandlerContext var1) throws Exception;

    /** @deprecated */
    @Deprecated
    void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception;

    @Inherited
    @Documented
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Sharable {
    }
}


Netty对责任处理接口做了更细粒度的划分,处理器被分成了两种,一种是入栈处理器

ChannellnboundHandler, 另一种是出栈处理器ChannelOutboundHandler, 这两个接口都继承自

ChannelHandler.而所有的处理器最终都在添加在Pipeline上。所 以 ,添加删除责任处理器的接口的

行为在Netty的 Channelpipeline中的进行了规定:


public interface ChannelPipeline extends ChannelInboundInvoker,ChannelOutboundInvoker, Iterable<Entry<String, ChannelHandler>> {

    ChannelPipeline addFirst(String name, ChannelHandler handler);

    ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);

    ChannelPipeline addLast(String name, ChannelHandler handler);

    ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);

    ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
    ...
}


在默认是实现类中将所有的Handler都串成了一个链表:


public class DefaultChannelPipeline implements ChannelPipeline {

    static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);

    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;
    ...
}


在 Pipeline中的任意一个节点,只要我们不手动的往下传播下去,这个事件就会终止传播在当前节 点。对于入栈数据,默认会传递到尾节点进行回收。如果我们不进行下一步传播,事件就会终止在当前

节点。对于出栈数据把数据写会客户端也意味看事件的终止。


当然在很多安全框架中也会大量使用责任链模式,比如Spring Security. Apache Shiro都会用到

设计模式中的责任链模式,感兴趣的小伙伴可以尝试自己去研究一下。


大部分框架中无论怎么实现,所有的实现都是大同小异的。其实如果我们是站在设计者这个角度看

源码的话,对我们学习源码和编码内功是非常非常有益处的,因为这样,我们可以站在更高的角度来学

习优秀的思想,而不是钻到某一个代码细节里边。我们需要对所有的设计必须有一个宏观概念,这样学

习起来才更加轻松。


八、责任链模式的优缺点

优点:


1、将请求与处理解耦;


2、请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转

发给下一级节点对象;


3、 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果;


4、 链路结构灵活,可以通过改变链路结构动态地新增或删减责任;

5、 易于扩展新的请求处理类(节点),符合开闭原则。


缺点:


1、 责任链太长或者处理时间过长,会影响整体性能


2、 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃;


参考文献:策略模式和参考者模式


上一篇:适配器模式与桥接模式(1)


下一篇:享元模式与组合模式(2)