必读
本篇主要分析
设计模式之 职责链模式
。
什么人适合学习本章节呢。
- 从未接触过设计模式的同学
- 有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的实现原理,让我们一起来分析下。
- 在我们的职责链模式的分配中,某一个接收者总是保留下一个接收者的引用,方便我们使用的时候调用。其实在Koa中也是如此,虽然实现不像Java中那样,但是使用数组
middlewares
将每个插件保存,而且是顺序保存。 - 在职责链模式中如果想让整个链顺序执行,前一个接收者必须调用后一个接收者的引用方法。而在Koa中我们通过next进行关联。调用next才会调用下一个插件。
总体分析而言,个人感觉Koa中链式调用也是职责链模式一种体现。
4. 使用场景 以及缺点:
4.1 场景
- 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等
- 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器
- 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等
- 职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能
4.2 缺点
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用
5. 结束
对职责链模式分下大致就这样。上述的分析来自于源码实际案例 以及个人对模式理解,如果有何不对的地方欢迎评论区指正。