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

【设计模式】责任链模式(Chain of Responsibility Pattern)本文将阐述设计模式中的责任链模式,包括责任链模式的应用场景、责任链模式与构造者模式的结合、框架源码分析等,最后综合阐述下责任链模式的优缺点。 希望可以帮忙大家更好的理解责任链模式。@空歌白石

前言

本文将阐述设计模式中的责任链模式,包括责任链模式的应用场景、责任链模式与构造者模式的结合、框架源码分析等,最后综合阐述下责任链模式的优缺点。

希望可以帮忙大家更好的理解责任链模式。@空歌白石

责任链模式定义

责任链模式Chain of Responsibility Pattern)是将链中的每一个节点看做成一个对象,每个节点处理的请求均不同,且内部自动维护一个下一个节点对象,当一个请求从链式的首端发出时,会沿着链路的路径一次传递给每一个节点对象,直至有对象处理这个请求为止。

责任链模式是一种行为型设计模式。

应用场景

总结下来责任链模式抽象的应用场景:

  1. 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态指定一组对象处理请求。

现实生活场景

审批流程

在公司或公共机构办理事务时,通常会有很长很长的流程。一开始可能由保安或客服中心给你有流程的图纸,你按照图纸上的顺序,一个窗口一个窗口,或者一个部门一个部门去办理业务,每办理完成一个业务,对应的业务员就在图纸上盖一个章,代表已经处理完成。盖这个账很重要,表示了一种责任,如果出了问题是要负责的。但所有的流程都办理完成后,整个事项也就办理完成了。

image.png

过关打BOSS

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

image.png

责任链模式实现

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

抽象处理者(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);
  }

  @Override
  public abstract String toString();
}

具体处理者(ConcreteHandler)角色:

具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下一个节点。由于具体处理者持有对下一节点的引用,因此,如果需要,具体处理者可以访问下一节点。

本文定义了SoldierOfficerCommander三种处理者。

public class Soldier extends RequestHandler {

  public Soldier(RequestHandler handler) {
    super(handler);
  }

  @Override
  public void handleRequest(Request req) {
    if (RequestType.COLLECT_TAX == req.getRequestType()) {
      printHandling(req);
      req.markHandled();
    } else {
      super.handleRequest(req);
    }
  }

  @Override
  public String toString() {
    return "soldier";
  }
}
public class Officer extends RequestHandler {

  public Officer(RequestHandler handler) {
    super(handler);
  }

  @Override
  public void handleRequest(Request req) {
    if (RequestType.TORTURE_PRISONER == req.getRequestType()) {
      printHandling(req);
      req.markHandled();
    } else {
      super.handleRequest(req);
    }
  }

  @Override
  public String toString() {
    return "officer";
  }

}
public class Commander extends RequestHandler {

  public Commander(RequestHandler handler) {
    super(handler);
  }

  @Override
  public void handleRequest(Request req) {
    if (RequestType.DEFEND_CASTLE == req.getRequestType()) {
      printHandling(req);
      req.markHandled();
    } else {
      super.handleRequest(req);
    }
  }

  @Override
  public 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<T> {

    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);
    }

    @Override
    public abstract String toString();

    public static class Builder<T> {

        private RequestHandler<T> head;

        private RequestHandler<T> tail;

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

        public Builder<T> addHandler(RequestHandler<T> 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();
}

image.png

框架源码分析

上文中自己实现了责任链模式,在很多的开源框架中也在大量的使用责任链模式。本小节,结合三种开源代码对于责任链的实现,进一步加深对责任链模式的理解和领悟。

当然责任链模式在开源框架中的实现绝不仅仅只有这三种,其他类似与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 filter
    if (_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 filter
        if (baseRequest.isAsyncSupported() && !_filterHolder.isAsyncSupported())
        { 
            try
            {
                baseRequest.setAsyncSupported(false,_filterHolder.toString());
                filter.doFilter(request, response, _next);
            }
            finally
            {
                baseRequest.setAsyncSupported(true,null);
            }
        }
        else
            filter.doFilter(request, response, _next);

        return;
    }

    // Call servlet
    HttpServletRequest 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);
    }
}

通过分析ServletHandlerdoFilter方法的引用可以看出,doFilter被不同的Handler实现.

image.png

最终我们可以找到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;
   }

   @Override
   public 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源码

image.png

Netty中有一个典型的责任链实现ChannelPipeline。具体的链路如下:

* <pre>
*                                                 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)            |
*  +-------------------------------------------------------------------+
* </pre>

ChannelHandler是Netty提供的默认处理者。典型的实现是ChannelInboundHandlerChannelOutboundHandler

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. */
    @Deprecated
    void 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. * <p> * 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. * <p> * This annotation is provided for documentation purpose, just like * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>. */
    @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<Map<Class<?>, String>> nameCaches =
            new FastThreadLocal<Map<Class<?>, String>>() {
        @Override
        protected Map<Class<?>, String> initialValue() {
            return new WeakHashMap<Class<?>, String>();
        }
    };

    private static final AtomicReferenceFieldUpdater<DefaultChannelPipeline, MessageSizeEstimator.Handle> ESTIMATOR =
            AtomicReferenceFieldUpdater.newUpdater(
                    DefaultChannelPipeline.class, MessageSizeEstimator.Handle.class, "estimatorHandle");
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

    private final Channel channel;
    private final ChannelFuture succeededFuture;
    private final VoidChannelPromise voidPromise;
    private final boolean touch = ResourceLeakDetector.isEnabled();

    private Map<EventExecutorGroup, EventExecutor> childExecutors;
    private volatile MessageSizeEstimator.Handle estimatorHandle;
    private boolean firstRegistration = true;

    /** * This is the head of a linked list that is processed by {@link #callHandlerAddedForAllHandlers()} and so process * all the pending {@link #callHandlerAdded0(AbstractChannelHandlerContext)}. * * We only keep the head because it is expected that the list is used infrequently and its size is small. * Thus full iterations to do insertions is assumed to be a good compromised to saving memory and tail management * complexity. */
    private PendingHandlerCallback pendingHandlerCallbackHead;

    /** * Set to {@code true} once the {@link AbstractChannel} is registered.Once set to {@code true} the value will never * change. */
    private boolean registered;

    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;
    }

BT源码

bt中初始化处理的工厂类中,按照如下方式构建了责任链。

public class TorrentProcessorFactory implements ProcessorFactory {

    private Map<Class<?>, Processor<?>> processors() {
        Map<Class<?>, Processor<?>> processors = new HashMap<>();

        processors.put(TorrentContext.class, createTorrentProcessor());
        processors.put(MagnetContext.class, createMagnetProcessor());

        return processors;
    }

    protected ChainProcessor<TorrentContext> createTorrentProcessor() {

        ProcessingStage<TorrentContext> stage5 = new SeedStage<>(null, torrentRegistry);

        ProcessingStage<TorrentContext> stage4 = new ProcessTorrentStage<>(stage5, torrentRegistry, trackerService, eventSink);

        ProcessingStage<TorrentContext> stage3 = new ChooseFilesStage<>(stage4, torrentRegistry, assignmentFactory, config);

        ProcessingStage<TorrentContext> stage2 = new InitializeTorrentProcessingStage<>(stage3, connectionPool,
                torrentRegistry, dataWorker, bufferedPieceRegistry, manualControlService, eventSink, config);

        ProcessingStage<TorrentContext> stage1 = new CreateSessionStage<>(stage2, torrentRegistry, eventSource,
                connectionSource, messageDispatcher, messagingAgents, config);

        ProcessingStage<TorrentContext> stage0 = new FetchTorrentStage(stage1, eventSink);

        return new ChainProcessor<>(stage0, executor, new TorrentContextFinalizer<>(torrentRegistry, eventSink));
    }

    protected ChainProcessor<MagnetContext> createMagnetProcessor() {

        ProcessingStage<MagnetContext> stage5 = new SeedStage<>(null, torrentRegistry);

        ProcessingStage<MagnetContext> stage4 = new ProcessMagnetTorrentStage(stage5, torrentRegistry, trackerService, eventSink);

        ProcessingStage<MagnetContext> stage3 = new ChooseFilesStage<>(stage4, torrentRegistry, assignmentFactory, config);

        ProcessingStage<MagnetContext> stage2 = new InitializeMagnetTorrentProcessingStage(stage3, connectionPool,
                torrentRegistry, dataWorker, bufferedPieceRegistry, manualControlService, eventSink, config);

        ProcessingStage<MagnetContext> stage1 = new FetchMetadataStage(stage2, metadataService, torrentRegistry,
                peerRegistry, eventSink, eventSource, config);

        ProcessingStage<MagnetContext> 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 <C> Type of processing context * @since 1.3 */
public interface ProcessingStage<C extends ProcessingContext> {

    /** * @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<C> execute(C context);
}

具体的链式调用实现。由process方法调用executeStage,当存在next节点后,继续调用executeStage。直到链路调用结束。

@Override
public CompletableFuture<?> process(C context, ListenerSource<C> listenerSource) {
    Runnable r = () -> executeStage(chainHead, context, listenerSource);
    return CompletableFuture.runAsync(r, executor);
}

private void executeStage(ProcessingStage<C> chainHead, C context, ListenerSource<C> listenerSource) {
    ProcessingEvent stageFinished = chainHead.after();
    Collection<BiFunction<C, ProcessingStage<C>, ProcessingStage<C>>> listeners;
    if (stageFinished != null) {
        listeners = listenerSource.getListeners(stageFinished);
    } else {
        listeners = Collections.emptyList();
    }

    ProcessingStage<C> next = doExecute(chainHead, context, listeners);
    if (next != null) {
        executeStage(next, context, listenerSource);
    }
}

优缺点

没有任何一种设计模式是万能的,所有的模式都需要结合实际使用。本章节将综合介绍下责任链模式的优缺点。

优点

  1. 将请求与处理解耦。
  2. 请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级的next节点对象即可。
  3. 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求在最终链路处理完成后的处理结果。
  4. 链路结构灵活,可以通过改变链路结构动态地新增或删除责任的具体节点。
  5. 易于扩展新的请求处理能力,符合设计模式的开闭原则。

缺点

  1. 责任链太长或者处理时间过长,会影响整体性能。
  2. 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。

结束语

设计模式可以使得代码更加优雅,增强代码的扩展性。但是万万不能为了设计模式而设计模式,如果真的这样做了,反而会适得其反,画蛇添足,大幅度增加代码复杂度和降低可维护性。只有在充分的分析业务场景、代码结构的前提下合理的使用设计模式,才能发挥出设计模式最大的作用。

最后一句:设计模式是道法,并不是术法。理解内涵最为重要。

192e0e94cf70d76b0fafc55b59fa1ad2.jpeg

今天的文章【设计模式】责任链模式(Chain of Responsibility Pattern)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22862.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注