【设计模式】责任链模式(Chain of Responsibility Pattern)

前言
本文将阐述设计模式中的责任链模式,包括责任链模式的应用场景、责任链模式与构造者模式的结合、框架源码分析等,最后综合阐述下责任链模式的优缺点。
希望可以帮忙大家更好的理解责任链模式。
责任链模式定义
责任链模式(Chain of Responsibility Pattern)是将链中的每一个节点看做成一个对象,每个节点处理的请求均不同,且内部自动维护一个下一个节点对象,当一个请求从链式的首端发出时,会沿着链路的路径一次传递给每一个节点对象,直至有对象处理这个请求为止。
责任链模式是一种行为型设计模式。
应用场景
总结下来责任链模式抽象的应用场景:
- 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
现实生活场景
审批流程
在公司或公共机构办理事务时,通常会有很长很长的流程。一开始可能由保安或客服中心给你有流程的图纸,你按照图纸上的顺序,一个窗口一个窗口,或者一个部门一个部门去办理业务,每办理完成一个业务,对应的业务员就在图纸上盖一个章,代表已经处理完成。盖这个账很重要,表示了一种责任,如果出了问题是要负责的。但所有的流程都办理完成后,整个事项也就办理完成了。

过关打BOSS
相信很多朋友在平常闲暇时也会打打游戏放松放松,其中有一种游戏类型就是通关型游戏。每关的BOSS都会对这一关负责,只有打败了这一关的BOSS才能继续,每关的BOSS就负责这一关的任务,也可以简单看成一个责任链。

责任链模式实现
要实现责任链模式,首先需要定义具体的链路责任。看下继承关系图:

抽象处理者(Handler)角色:
定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
具体实现类RequestHandler如下,核心是next属性。
public abstract class RequestHandler {private final RequestHandler next;/*** Request handler.*/public void handleRequest(Request req) {if (next != null) {next.handleRequest(req);}}protected void printHandling(Request req) {LOGGER.info("{} handling request "{}"", this, req);}@Overridepublic abstract String toString();
}
具体处理者(ConcreteHandler)角色:
具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下一个节点。由于具体处理者持有对下一节点的引用,因此,如果需要,具体处理者可以访问下一节点。
本文定义了Soldier,Officer,Commander三种处理者。
public class Soldier extends RequestHandler {public Soldier(RequestHandler handler) {super(handler);}@Overridepublic void handleRequest(Request req) {if (RequestType.COLLECT_TAX == req.getRequestType()) {printHandling(req);req.markHandled();} else {super.handleRequest(req);}}@Overridepublic String toString() {return "soldier";}
}
public class Officer extends RequestHandler {public Officer(RequestHandler handler) {super(handler);}@Overridepublic void handleRequest(Request req) {if (RequestType.TORTURE_PRISONER == req.getRequestType()) {printHandling(req);req.markHandled();} else {super.handleRequest(req);}}@Overridepublic String toString() {return "officer";}}
public class Commander extends RequestHandler {public Commander(RequestHandler handler) {super(handler);}@Overridepublic void handleRequest(Request req) {if (RequestType.DEFEND_CASTLE == req.getRequestType()) {printHandling(req);req.markHandled();} else {super.handleRequest(req);}}@Overridepublic String toString() {return "commander";}
}
上文中定义了具体的处理者。如何将处理者串联起来呢?我们定义了如下的King类,通过buildChain方法实现链路的串联。
public class King {private RequestHandler chain;public King() {buildChain();}private void buildChain() {chain = new Commander(new Officer(new Soldier(null)));}public void makeRequest(Request req) {chain.handleRequest(req);}}
责任链模式与建造者模式结合
上文中我们简单实现了责任链模式。但是上述的实现真的优雅吗?
有些同学可能已经发现。在最终构建chain时,使用new Commander(new Officer(new Soldier(null)))方式实现的看着很不爽,特别是,这里有三种处理者还比较容易编写,但是当有更多的处理者时,这个构建就会相当的复杂。
因此,我们可以将责任链模式和构造者模式相结合。这样可以使得代码变得更加优雅,扩展性更好,仅需要addHandler即可。
第一步:调整RequestHandler,增加一个内部类Builder。
public abstract class RequestHandler {protected RequestHandler next;/*** Request handler.*/public void handleRequest(Request req) {if (next != null) {next.handleRequest(req);}}protected void printHandling(Request req) {LOGGER.info("{} handling request "{}"", this, req);}@Overridepublic abstract String toString();public static class Builder {private RequestHandler head;private RequestHandler tail;public RequestHandler build() {return this.head;}public Builder addHandler(RequestHandler handler) {if (this.head == null) {this.head = this.tail = handler;}this.tail.next = handler;this.tail = handler;return this;}}
}
第二步:修改build方法,与上文的不断new的方法相比更加优雅。
private void buildChain() {RequestHandler.Builder builder = new RequestHandler.Builder();builder.addHandler(new Commander()).addHandler(new Officer()).addHandler(new Soldier());chain = builder.build();
}

框架源码分析
上文中自己实现了责任链模式,在很多的开源框架中也在大量的使用责任链模式。本小节,结合三种开源代码对于责任链的实现,进一步加深对责任链模式的理解和领悟。
当然责任链模式在开源框架中的实现绝不仅仅只有这三种,其他类似与Spring等框架也都在大量使用,大家可以按照实际的需求具体分析。
Servlet源码
servlet包中有Filter接口,其中的doFilter使用了FilterChain,从名称上我们也可以看出实现了一个过滤链。
public interface Filter {// 省去无关代码 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;// 省去无关代码
}
servlet仅仅定义了FilterChain的接口,具体的实现开放给具体的适用方。
package javax.servlet;import java.io.IOException;/*** A FilterChain is an object provided by the servlet container to the developer* giving a view into the invocation chain of a filtered request for a resource. Filters* use the FilterChain to invoke the next filter in the chain, or if the calling filter* is the last filter in the chain, to invoke the resource at the end of the chain.** @see Filter* @since Servlet 2.3**/public interface FilterChain {/*** Causes the next filter in the chain to be invoked, or if the calling filter is the last filter* in the chain, causes the resource at the end of the chain to be invoked.** @param request the request to pass along the chain.* @param response the response to pass along the chain.*/public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;}
以jettey包中的ServletHandler的实现为例,继续介绍FilterChain的实现。
public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException
{final Request baseRequest=Request.getBaseRequest(request);// pass to next filterif (_filterHolder!=null){if (LOG.isDebugEnabled())LOG.debug("call filter {}", _filterHolder);Filter filter= _filterHolder.getFilter();//if the request already does not support async, then the setting for the filter//is irrelevant. However if the request supports async but this filter does not//temporarily turn it off for the execution of the filterif (baseRequest.isAsyncSupported() && !_filterHolder.isAsyncSupported()){ try{baseRequest.setAsyncSupported(false,_filterHolder.toString());filter.doFilter(request, response, _next);}finally{baseRequest.setAsyncSupported(true,null);}}elsefilter.doFilter(request, response, _next);return;}// Call servletHttpServletRequest srequest = (HttpServletRequest)request;if (_servletHolder == null)notFound(baseRequest, srequest, (HttpServletResponse)response);else{if (LOG.isDebugEnabled())LOG.debug("call servlet " + _servletHolder);_servletHolder.handle(baseRequest,request, response);}
}
通过分析ServletHandler中doFilter方法的引用可以看出,doFilter被不同的Handler实现.

最终我们可以找到FilterChain的具体实现类VirtualFilterChain:
private static class VirtualFilterChain implements FilterChain {private final FilterChain originalChain;private final List extends Filter> additionalFilters;private int currentPosition = 0;public VirtualFilterChain(FilterChain chain, List extends Filter> additionalFilters) {this.originalChain = chain;this.additionalFilters = additionalFilters;}@Overridepublic void doFilter(final ServletRequest request, final ServletResponse response)throws IOException, ServletException {if (this.currentPosition == this.additionalFilters.size()) {this.originalChain.doFilter(request, response);}else {this.currentPosition++;Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);nextFilter.doFilter(request, response, this);}}
}
VirtualFilterChain是将所有的Filter存储在additionalFilters的集合中。从doFilter作为入口,开始责任链的传递和工作。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {new VirtualFilterChain(chain, this.filters).doFilter(request, response);
}
Netty源码

在Netty中有一个典型的责任链实现ChannelPipeline。具体的链路如下:
*
* I/O Request
* via {@link Channel} or
* {@link ChannelHandlerContext}
* |
* +---------------------------------------------------+---------------+
* | ChannelPipeline | |
* | |/ |
* | +---------------------+ +-----------+----------+ |
* | | Inbound Handler N | | Outbound Handler 1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | |/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler N-1 | | Outbound Handler 2 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ . |
* | . . |
* | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
* | [ method call] [method call] |
* | . . |
* | . |/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 2 | | Outbound Handler M-1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | |/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 1 | | Outbound Handler M | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* +---------------+-----------------------------------+---------------+
* | |/
* +---------------+-----------------------------------+---------------+
* | | | |
* | [ Socket.read() ] [ Socket.write() ] |
* | |
* | Netty Internal I/O Threads (Transport Implementation) |
* +-------------------------------------------------------------------+
*
ChannelHandler是Netty提供的默认处理者。典型的实现是ChannelInboundHandler和ChannelOutboundHandler。
public interface ChannelHandler {/*** Gets called after the {@link ChannelHandler} was added to the actual context and it's ready to handle events.*/void handlerAdded(ChannelHandlerContext ctx) throws Exception;/*** Gets called after the {@link ChannelHandler} was removed from the actual context and it doesn't handle events* anymore.*/void handlerRemoved(ChannelHandlerContext ctx) throws Exception;/*** Gets called if a {@link Throwable} was thrown.** @deprecated if you want to handle this event you should implement {@link ChannelInboundHandler} and* implement the method there.*/@Deprecatedvoid exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;/*** Indicates that the same instance of the annotated {@link ChannelHandler}* can be added to one or more {@link ChannelPipeline}s multiple times* without a race condition.* * If this annotation is not specified, you have to create a new handler* instance every time you add it to a pipeline because it has unshared* state such as member variables.*
* This annotation is provided for documentation purpose, just like* the JCIP annotations.*/@Inherited@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface Sharable {// no value}
}
ChannelPipeline可以看做是整个处理任务的chain管理者,netty提供的的默认实现DefaultChannelPipeline如下。
public class DefaultChannelPipeline implements ChannelPipeline {static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);private static final String HEAD_NAME = generateName0(HeadContext.class);private static final String TAIL_NAME = generateName0(TailContext.class);private static final FastThreadLocal
BT源码
在bt中初始化处理的工厂类中,按照如下方式构建了责任链。
public class TorrentProcessorFactory implements ProcessorFactory {private Map, Processor>> processors() {Map, Processor>> processors = new HashMap<>();processors.put(TorrentContext.class, createTorrentProcessor());processors.put(MagnetContext.class, createMagnetProcessor());return processors;}protected ChainProcessor createTorrentProcessor() {ProcessingStage stage5 = new SeedStage<>(null, torrentRegistry);ProcessingStage stage4 = new ProcessTorrentStage<>(stage5, torrentRegistry, trackerService, eventSink);ProcessingStage stage3 = new ChooseFilesStage<>(stage4, torrentRegistry, assignmentFactory, config);ProcessingStage stage2 = new InitializeTorrentProcessingStage<>(stage3, connectionPool,torrentRegistry, dataWorker, bufferedPieceRegistry, manualControlService, eventSink, config);ProcessingStage stage1 = new CreateSessionStage<>(stage2, torrentRegistry, eventSource,connectionSource, messageDispatcher, messagingAgents, config);ProcessingStage stage0 = new FetchTorrentStage(stage1, eventSink);return new ChainProcessor<>(stage0, executor, new TorrentContextFinalizer<>(torrentRegistry, eventSink));}protected ChainProcessor createMagnetProcessor() {ProcessingStage stage5 = new SeedStage<>(null, torrentRegistry);ProcessingStage stage4 = new ProcessMagnetTorrentStage(stage5, torrentRegistry, trackerService, eventSink);ProcessingStage stage3 = new ChooseFilesStage<>(stage4, torrentRegistry, assignmentFactory, config);ProcessingStage stage2 = new InitializeMagnetTorrentProcessingStage(stage3, connectionPool,torrentRegistry, dataWorker, bufferedPieceRegistry, manualControlService, eventSink, config);ProcessingStage stage1 = new FetchMetadataStage(stage2, metadataService, torrentRegistry,peerRegistry, eventSink, eventSource, config);ProcessingStage stage0 = new CreateSessionStage<>(stage1, torrentRegistry, eventSource,connectionSource, messageDispatcher, messagingAgents, config);return new ChainProcessor<>(stage0, executor, new TorrentContextFinalizer<>(torrentRegistry, eventSink));}
BT定义的抽象处理者(Handler)角色为ProcessingStage。使用after()表示next。
package bt.processor;import bt.processor.listener.ProcessingEvent;/*** @param Type of processing context* @since 1.3*/
public interface ProcessingStage {/*** @return Type of event, that should be triggered after this stage has completed.* @since 1.5*/ProcessingEvent after();/*** @param context Processing context* @return Next stage* @since 1.3*/ProcessingStage execute(C context);
}
具体的链式调用实现。由process方法调用executeStage,当存在next节点后,继续调用executeStage。直到链路调用结束。
@Override
public CompletableFuture> process(C context, ListenerSource listenerSource) {Runnable r = () -> executeStage(chainHead, context, listenerSource);return CompletableFuture.runAsync(r, executor);
}private void executeStage(ProcessingStage chainHead,C context,ListenerSource listenerSource) {ProcessingEvent stageFinished = chainHead.after();Collection, ProcessingStage>> listeners;if (stageFinished != null) {listeners = listenerSource.getListeners(stageFinished);} else {listeners = Collections.emptyList();}ProcessingStage next = doExecute(chainHead, context, listeners);if (next != null) {executeStage(next, context, listenerSource);}
}
优缺点
没有任何一种设计模式是万能的,所有的模式都需要结合实际使用。本章节将综合介绍下责任链模式的优缺点。
优点
- 将请求与处理解耦。
- 请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级的next节点对象即可。
- 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求在最终链路处理完成后的处理结果。
- 链路结构灵活,可以通过改变链路结构动态地新增或删除责任的具体节点。
- 易于扩展新的请求处理能力,符合设计模式的开闭原则。
缺点
- 责任链太长或者处理时间过长,会影响整体性能。
- 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。
结束语
设计模式可以使得代码更加优雅,增强代码的扩展性。但是万万不能为了设计模式而设计模式,如果真的这样做了,反而会适得其反,画蛇添足,大幅度增加代码复杂度和降低可维护性。只有在充分的分析业务场景、代码结构的前提下合理的使用设计模式,才能发挥出设计模式最大的作用。
最后一句:设计模式是道法,并不是术法。理解内涵最为重要。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
