设计模式--责任链模式

简介

​ 责任链模式(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、如果节点对象存在循环引用,会造成死循环,导致系统崩溃。

代码链接

责任链模式

posted @ 2021-07-27 23:12  snail灬  阅读(886)  评论(0编辑  收藏  举报