责任链模式(Chain of Responsibility Pattern)也叫职责链模式:是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,知道有对象处理这个请求为止。属于行为型模式。
1、工作中的审批流程
2、游戏中的闯关
责任链模式主要是解耦了请求与处理,客户只需将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象进行处理。适用于以下应用场景:
1、多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定;
2、在不明确指定接受者的情况下,向多个对象中的一个提交一个请求
3、可动态指定一组对象处理请求
从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只是定义了一个规范,具体处理逻辑是由使用者自己来实现。
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、如果节点对象存在循环引用,会造成死循环,导致系统崩溃。
责任链模式