zoukankan      html  css  js  c++  java
  • 设计模式--责任链模式

    简介

    ​ 责任链模式(Chain of Responsibility Pattern)也叫职责链模式:是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,知道有对象处理这个请求为止。属于行为型模式。

    责任链的应用场景

    1、工作中的审批流程

    2、游戏中的闯关

    责任链模式主要是解耦了请求与处理,客户只需将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象进行处理。适用于以下应用场景:

    1、多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定;

    2、在不明确指定接受者的情况下,向多个对象中的一个提交一个请求

    3、可动态指定一组对象处理请求

    责任链模式的UML类图:

    从UML类图,我们可以看到,责任链模式只要包含两种角色:

    抽象处理者(Handler):定义一个请求处理的方法,并维护一个下一个处理节点Handler对象的引用;

    具体处理者(ConcreteHandler):对请求进行处理,如果不感兴趣,则进行转发。

    责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解的是其模式而不是具体实现,责任链模式的独到之处是其将节点处理者组成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动了起来。

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

    1、JDK中的应用,Filter类

    public interface Filter {
    
        public default void init(FilterConfig filterConfig) throws ServletException {}
    
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException;
    
        public default void destroy() {}
        
    }
    

    这个接口相当于责任链模型中的Handler抽象角色。通过doFilter()方法形成一条责任链。

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

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

    • Spring中的实现
    public class MockFilterChain implements FilterChain {
    
    	@Nullable
    	private ServletRequest request;
    
    	@Nullable
    	private ServletResponse response;
    
    	private final List<Filter> filters;
    
    	@Nullable
    	private Iterator<Filter> iterator;
    
        ...
            
    	@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;
    	}
        
        private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
    		Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
    		return Arrays.asList(allFilters);
    	}
        
        ...
    }
    

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

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

    Netty的责任处理器接口ChannelHandler

    public interface ChannelHandler {
    
        void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    
        void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
    
        @Deprecated
        void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
    
      
        @Inherited
        @Documented
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @interface Sharable {
            // no value
        }
    }
    

    Netty对责任处理接口做了更细粒度的划分,处理器被分成了两种,一种是入栈处理器ChannelInboundHandler,另一种是出栈处理器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);
    
        ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
        
        ...
    }
    
    

    在默认实现类中将所有的Handler都串了起来(链表):

    public class DefaultChannelPipeline implements ChannelPipeline {
        
        ...
            
        final AbstractChannelHandlerContext head;
        final AbstractChannelHandlerContext tail;
        
        ...
            
        protected DefaultChannelPipeline(Channel channel) {
            this.channel = ObjectUtil.checkNotNull(channel, "channel");
            succeededFuture = new SucceededChannelFuture(channel, null);
            voidPromise =  new VoidChannelPromise(channel, true);
    
            tail = new TailContext(this);
            head = new HeadContext(this);
    
            head.next = tail;
            tail.prev = head;
        }
        
        ...
        
    }
    

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

    责任链模式的优缺点:

    优点:

    1、将请求与处理解耦

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

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

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

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

    缺点:

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

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

    代码链接

    责任链模式

  • 相关阅读:
    CSS盒子模式(DIV布局快速入门)
    CSS中的滑动门技术
    由浅入深漫谈margin属性
    zz Apache 虚拟主机 VirtualHost 配置
    动态生成编译运行java类
    ubuntu 手动设置DNS服务器,重启后不能上网
    ubuntu下部署发布环境
    zz [Java]读取文件方法大全
    Ubuntu apache2 主机配置文件
    JAVA的CALLBACK
  • 原文地址:https://www.cnblogs.com/snail-gao/p/15068389.html
Copyright © 2011-2022 走看看