您现在的位置是:首页 >学无止境 >设计模式系列/ 职责链模式网站首页学无止境

设计模式系列/ 职责链模式

浩哥说技术 2024-06-17 11:28:01
简介设计模式系列/ 职责链模式

必读

本篇主要分析设计模式之 职责链模式
什么人适合学习本章节呢。

  • 从未接触过设计模式的同学
  • 有n年工作经验 && 对职责链模式不了解的同学

1. 职责链模式意识形态

设计模式充斥着我们开发过程中的方方面面,责任链模式也是如此。也许你用到了设计模式,只是不知道这是哪种设计模式,甚至不知道这是设计模式。
所以我们今天的目的也是为了解开谜底 的。

在这里插入图片描述

我们可以将职责链的部分, 理解为相连的链表。每个接收者 只关心自己后面的部分。如果依次顺序执行以后会执行到最后的。

在这里插入图片描述

通过上述截图我们可以清晰的看到:每个接收者都可以直接引用到下一个接收者,但是也有可能下一个接收者为null。
所以对于职责链而言,只需要关心下一个接收者就够了。

但是不可能想干啥就干啥,一定是在一个规范下进行定义的。
在这里插入图片描述

通过以上的截图我们分析得到,职责链中有几种角色是必须的:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,比如上图中的successor)
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程

以上角色的划分 比较符合我们对职责链的期望。比如:我们使用变量successor 来保存下一个接收者的引用。
好了 废话不错说了,我们来列举一个小小的案例。

2. 小小的案例

定义抽象类,用来指定行为规范。

public abstract class Handler {
  protected Handler next;
  
  public void setNext(Handler next) {
    this.next = next;
  }
  
  public abstract void handleRequest(String request);
}

行为实现A

public class HandleImplA extends Handler {
  @Override
  public void handleRequest(String request) {
    System.out.println("replace str A");
    request = request.replace("A", "");
    
    if (this.next != null) {
      this.next.handleRequest(request);
    }
  }
}

行为实现B

public class HandleImplB extends Handler {
  @Override
  public void handleRequest(String request) {
    System.out.println("replace str B");
    request = request.replace("B", "");
    
    if (this.next != null) {
      this.next.handleRequest(request);
    }
  }
}

client 端 发送链请求

public class Test001 {
  
  @Test
  public void test01() {
    Handler handleImplA = new HandleImplA();
    Handler handleImplB = new HandleImplB();
    
    handleImplA.setNext(handleImplB);
    handleImplA.handleRequest("ABC");
  }
}

在这里插入图片描述

通过上述案例可以分析得知:

  • 我们每个都需要实现抽象类Handler, 因为抽象类中规范了我们的行为,例如:引用了下一个接收者,以及必须定义事件处理方法。
  • 每个具体的类 都需要实现这个抽象类
  • 所以对于每个接收者来说,我们只需要关心下一个接收者是否能够执行就够了。

3. 业务场景中案例

3.1 场景1

public class AInvalidPathFilter implements Filter {
  
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    filterChain.doFilter(servletRequest, servletResponse);
  }
}

无论是我们Java开发中的拦截器,还是过滤器 都是职责链模式的一种体现。核心的逻辑是不变化,只不过是代码的实现方式发生了变化。

3.2 场景2

不知道大家是否知道前端中的Koa框架。个人拙见 其实Koa的链式方法调用 也是一种职责链模式的体现,让我们一起来分析下。

// ------------------- 定义
import  Koa from "koa"
import Router from "koa-router"
import bodyParser from "koa-bodyparser"

const app = new Koa()
app.use(Router())
app.use(bodyParser())


// ------------------- 使用
// 例如 Router

const Router = () => (ctx, next) => {
  // 手动执行next 方法才会执行下一步
  next();
}


// ------------------- 内部原理
const middlewares = []; // 所有的中间件
const ctx = {} // 表示koa上下文
const use = (index) => {
  const fn = middlewares[index];
  if (!fn) return;
  fn(ctx, () => use(index + 1));
}

// 第一次执行
use(0);

上述代码就是最简单的Koa的实现原理,让我们一起来分析下。

  1. 在我们的职责链模式的分配中,某一个接收者总是保留下一个接收者的引用,方便我们使用的时候调用。其实在Koa中也是如此,虽然实现不像Java中那样,但是使用数组middlewares 将每个插件保存,而且是顺序保存。
  2. 在职责链模式中如果想让整个链顺序执行,前一个接收者必须调用后一个接收者的引用方法。而在Koa中我们通过next进行关联。调用next才会调用下一个插件。

总体分析而言,个人感觉Koa中链式调用也是职责链模式一种体现。

4. 使用场景 以及缺点:

4.1 场景

  1. 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等
  2. 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器
  3. 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等
  4. 职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能

4.2 缺点

  1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用

5. 结束

对职责链模式分下大致就这样。上述的分析来自于源码实际案例 以及个人对模式理解,如果有何不对的地方欢迎评论区指正。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。