Java设计模式之责任链模式的概念、实现以及netty中的责任链模式

作者:易(Master Yi) 时间:2022-05-28 00:24:12 

目录
  • 1、概念相关

    • 1.1、概念

    • 1.2、解决了什么:

    • 1.3、场景:

  • 2、简单实现

    • 2.1 代码

  • 3. netty中的责任链模式

    • 4、思考

      本文先介绍了责任链模式的概念及简单实现。再贴了netty中对责任链的实现。最后总结了一点点思考。

      1、概念相关

      1.1、概念

      责任链模式为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,沿着这条链传递请求,直到有对象处理它为止。

      1.2、解决了什么:

      客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

      1.3、场景:

      1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。

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

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

      2、简单实现

      2.1 代码

      注:代码中getter 和 setter都省略

      定义一个请求


      public class Request {

      private String name;

      private int days;

      }

      定义一个返回结果


      public class Result {

      private boolean agree;

      public Result(boolean agree) {
      this.agree = agree;
      }
      }

      定义一个处理接口


      public interface Handler {
      // 每个处理器持有链,能取到链上的请求或者传递请求
      Result deal(Chain chain);

      interface Chain {
      // 获取请求
      Request request();
      // 传递请求
      Result proceed(Request request);
      }
      }

      定义一个处理接口的链


      public class HandlerChain implements Handler.Chain {
      // 持有链要处理的请求
      private Request request;
      // 持有链上所有的处理器
      private Queue<Handler> handlers;

      public HandlerChain(Request request) {
      this.request = request;
      }
      // 添加一个处理器
      public HandlerChain addHandler(Handler handler) {
      if (handlers == null) {
      handlers = new LinkedList<>();
      }
      handlers.add(handler);
      return this;
      }
      // 实现接口的方法- 获取请求
      public Request request() {
      return request;
      }
      // 实现接口的方法- 处理请求
      public Result proceed(Request request) {
      // 取队首的处理器开始请求。如果队首的处理器处理了,直接返回结果
      // 如果队首的处理器选择传递请求,又会进这个proceed方法。取新的队首处理
      // 为什么是传递而不是直接遍历队列,如果result = null,继续;result != null,返回呢? 因为传递的方式,可以对request再次处理。A处理器可以做简单处理,再传递给B。
      Handler handler = handlers.poll();
      return handler.deal(this);
      }
      }

      定义三个处理者


      public class AHandler implements Handler{
      public Result deal(Chain chain) {
      Request request = chain.request();
      // 只处理小于等于1的请求,大于1的请求被传递了
      if (request.getDays() > 1) {
      // 这里可以对request做部分处理,再传递
      return chain.proceed(request);
      }
      System.out.println("A处理了");
      return new Result(true);
      }
      }

      public class BHandler implements Handler {
      public Result deal(Chain chain) {
      Request request = chain.request();
      // 只处理小于等于2的请求,大于2的请求被传递了
      if (request.getDays() > 2) {
      return chain.proceed(request);
      }
      System.out.println("B处理了");
      return new Result(true);
      }
      }

      public class CHandler implements Handler {
      public Result deal(Chain chain) {
      Request request = chain.request();
      // 只处理小于等于3的请求,大于3的请求被传递了
      if (request.getDays() > 3) {
      return chain.proceed(request);
      }
      System.out.println("C处理了");
      return new Result(true);
      }
      }

      测试


      public class Test {

      public static void main(String[] args) {
      // new 一个链,往链上添加处理器
      Request request1 = new Request("hhy", 3);
      HandlerChain chains = new HandlerChain(request1).addHandler(new AHandler()).addHandler(new BHandler()).addHandler(new CHandler());
      Result result1 = chains.proceed(request1);
      System.out.println("结果:" + result1.isAgree());

      }
      }

      结果

      传入3:
      返回:
      C处理了
      结果:true

      传入2:
      返回:
      B处理了
      结果:true

      传入1:
      返回:
      A处理了
      结果:true

      3. netty中的责任链模式

      用过netty的同学看到下面这个代码应该很熟悉了,非常简单netty客户端,创建连接,设置编解码器,发送请求。


      public static void main(String[] args) {
      EventLoopGroup group = new NioEventLoopGroup();
      NioSocketChannel channel = new NioSocketChannel();
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.group(group).option(ChannelOption.TCP_NODELAY, true);
      InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(httpRequest.host), httpRequest.port);
      group.register(channel);
      channel.connect(address).sync();
      channel.pipeline().addLast("http-decoder", new HttpResponseDecoder());
      channel.pipeline().addLast("http-encoder", new HttpRequestEncoder());
      channel.pipeline().addLast("http-client", new HttpHandler(HttpClient.this));
      channel.writeAndFlush(httpRequest);
      }

      通过我们上面的例子,不难猜测到 channel.pipeline() 应该就是一个链,持有了channel上所有的处理器。pipeline()方法返回一个ChannelPipeline接口,我们直接看它的实现类


      public class DefaultChannelPipeline implements ChannelPipeline {
      final AbstractChannelHandlerContext head;
      final AbstractChannelHandlerContext tail;

      @Override
      public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
      final AbstractChannelHandlerContext newCtx;
      synchronized (this) {
      checkMultiplicity(handler);
      // 把handler封装成AbstractChannelHandlerContext
      newCtx = newContext(group, filterName(name, handler), handler);
      // 调用了 addLast0方法
      addLast0(newCtx);
      ...
      }
      callHandlerAdded0(newCtx);
      return this;
      }

      private void addLast0(AbstractChannelHandlerContext newCtx) {
      // 把新加入的处理器设置成尾部的前驱,原尾部的前驱设置成新处理器的后继
      AbstractChannelHandlerContext prev = tail.prev;
      newCtx.prev = prev;
      newCtx.next = tail;
      prev.next = newCtx;
      tail.prev = newCtx;
      }
      }

      通过addLast0, 我们看到DefaultChannelPipeline使用了链表的形式存储了处理器。

      继续看这个类的其他方法


      public class DefaultChannelPipeline implements ChannelPipeline {
      ...
      @Override
      public final Channel channel() {
      return channel;
      }

      @Override
      public final ChannelPipeline fireChannelActive() {
      AbstractChannelHandlerContext.invokeChannelActive(head);
      return this;
      }
      ...
      }

      返回当前的channel,处理channel上的事件。(就类似于我们上面的链里面有getRequest(),proceed()方法 )

      继续跟踪invokeChannelActive方法


      abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
      static void invokeChannelActive(final AbstractChannelHandlerContext next) {
      EventExecutor executor = next.executor();
      if (executor.inEventLoop()) {
       next.invokeChannelActive();
      } else {
       executor.execute(new Runnable() {
       @Override
       public void run() {
        next.invokeChannelActive();
       }
       });
      }
      }
      // 调用了invokeChannelActive

      private void invokeChannelActive() {
      // 判断是不是要处理
      if (invokeHandler()) {
       try {
       // 处理
       ((ChannelInboundHandler) handler()).channelActive(this);
       } catch (Throwable t) {
       invokeExceptionCaught(t);
       }
      } else {
       // 事件传递
       fireChannelActive();
      }
      }

      @Override
      public ChannelHandlerContext fireChannelActive() {
      // 执行前需要先找到一个合适的处理器 invokeChannelActive
      invokeChannelActive(findContextInbound(MASK_CHANNEL_ACTIVE));
      return this;
      }

      private AbstractChannelHandlerContext findContextInbound(int mask) {
      AbstractChannelHandlerContext ctx = this;
      EventExecutor currentExecutor = executor();
      do {
       // 循环,找到一个合适的处理器并返回
       ctx = ctx.next;
      } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
      return ctx;
      }
      }

      其实看到这,这个链已经非常的明显了。 pipline持有处理器,AbstractChannelHandlerContext做了一些封装,使得链上的处理器能对事件进行传递和处理。

      最后再看下handler的实现类


      public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
      ......
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
      ctx.fireChannelActive();
      }
      ......
      }

      以这个ChannelInboundHandlerAdapter为例,在channel收到激活事件通知的时候,它调用了ctx.fireChannelActive();方法传递了事件。ctx是ChannelHandlerContext类型,很熟悉,它是个接口,我们上面看到的AbstractChannelHandlerContext是它的实现类。就这样,一个激活事件就在链上传递了起来。而链上的处理器就是我们最初始的测试方法里面addLast进去的。

      4、思考

      在netty中,事件在责任链中有序传播,事件处理器可以处理自己关心的功能,可以拦截,也可以继续传播(向前或向后)事件。上层的业务只需要关心自己的逻辑。整个架构层次分明。

      OA系统的工作流 似乎也特别适合责任链模式,正如我们一开始的例子,不同人审批不同的时长的假期。

      对于代码里某些冗长的if else ,是不是也有改造成责任链的可能?

      if (通过第一关) {
          进入第二关
          if (通过第二关) {
              进入第三关
              ...
          }
      }

      改成通过第一关后传递事件,反之结束。这期间还可以灵活的做跳跃,得到某个奖励,直接跳到第三关。如果要添加第四关,也比if else灵活的多。

      当然并不是if else 都适合改造成责任链模式,还是要具体的业务及效率上综合考虑。个人觉得在流程上有顺序依赖的,非常适合。

      [1] https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html

      [2] www.jb51.net/article/202504.htm

      来源:https://blog.csdn.net/hhy107107/article/details/107526126

      标签:netty,责任链模式,概念
      0
      投稿

      猜你喜欢

    • Java实战之飞翔的小鸟小游戏

      2022-10-04 20:37:20
    • Java使用Jdbc连接Oracle执行简单查询操作示例

      2021-07-25 03:34:25
    • Java多线程事务回滚@Transactional失效处理方案

      2021-06-20 19:19:28
    • Spring rest接口中的LocalDateTime日期类型转时间戳

      2022-11-20 14:00:00
    • Kotlin select使用方法介绍

      2022-05-28 19:34:27
    • 浅谈java多态的实现主要体现在哪些方面

      2023-08-17 07:41:32
    • C# this关键字的四种用法

      2022-10-06 07:09:22
    • c#扩展datatable转json示例

      2022-08-19 10:44:55
    • 详解spring cloud中使用Ribbon实现客户端的软负载均衡

      2022-10-26 15:54:45
    • Java KindEditor粘贴图片自动上传到服务器功能实现

      2023-08-07 01:42:33
    • 浅析SpringBoot2.4 静态资源加载问题

      2023-01-29 11:04:33
    • Java JDK与cglib动态代理有什么区别

      2023-07-23 08:10:15
    • MyBatis实现物理分页的实例

      2023-03-13 04:21:45
    • SpringBoot导入导出数据实现方法详解

      2023-10-03 19:47:22
    • Flutter控制组件显示和隐藏三种方式详解

      2023-07-07 03:03:30
    • unity实现贴图矩阵运算(旋转平移缩放)

      2022-07-27 13:53:41
    • C++右值引用与move和forward函数的使用详解

      2023-07-05 19:27:33
    • 直接在线预览Word、Excel、TXT文件之ASP.NET

      2021-10-07 15:37:54
    • 详细解读Java编程中面向字符的输入流

      2023-09-27 10:39:21
    • android读取Assets图片资源保存到SD卡实例

      2022-12-18 19:50:24
    • asp之家 软件编程 m.aspxhome.com