设计模式之责任链模式

发布时间 2023-12-11 12:23:55作者: 当时明月在曾照彩云归

1. 定义

请求沿着一条链传递,直到有一个对象能够处理它为止

2. 口语化表述

假设某公司的财务审批流程如下:

  • 项目经理可以审批1000元以下的费用,大于1000元的费用提交给部门经理审批
  • 部门经理可以审批10000元以下的费用,大于10000的费用提交给总经理审批
  • 总经理可以审批任何额度的费用

现在,员工报销费用时,只需要提交给自己的项目经理即可,无论数额多少,最终都会有合适的经理来处理

沿着一条链传递,这就是责任链模式

(下面的表述会沿用这个场景)

3. 源码示例

前端JavaScipt的原型链,也算是一种责任链模式

在JS中,每个对象都有一个原型对象,它可以作为对象的处理者来处理请求,如果当前对象无法处理请求,它会将请求转发给它的原型对象,直到找到一个能够处理请求的对象,如果在整个原型链上都找不到对应的属性或方法,JavaScript会返回undefined

MDN的原型链示例如下:

const o = {
  a: 1,
  b: 2,
  // __proto__ 设置了 [[Prototype]]。它在这里被指定为另一个对象字面量。
  __proto__: {
    b: 3,
    c: 4,
  },
};

// o.[[Prototype]] 具有属性 b 和 c。
// o.[[Prototype]].[[Prototype]] 是 Object.prototype(我们会在下文解释其含义)。
// 最后,o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null。
// 这是原型链的末尾,值为 null,
// 根据定义,其没有 [[Prototype]]。
// 因此,完整的原型链看起来像这样:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null

console.log(o.a); // 1
// o 上有自有属性“a”吗?有,且其值为 1。

console.log(o.b); // 2
// o 上有自有属性“b”吗?有,且其值为 2。
// 原型也有“b”属性,但其没有被访问。
// 这被称为属性遮蔽(Property Shadowing)

console.log(o.c); // 4
// o 上有自有属性“c”吗?没有,检查其原型。
// o.[[Prototype]] 上有自有属性“c”吗?有,其值为 4。

console.log(o.d); // undefined
// o 上有自有属性“d”吗?没有,检查其原型。
// o.[[Prototype]] 上有自有属性“d”吗?没有,检查其原型。
// o.[[Prototype]].[[Prototype]] 是 Object.prototype 且
// 其默认没有“d”属性,检查其原型。
// o.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索,
// 未找到该属性,返回 undefined。

DOM事件处理机制可以被看作是一种责任链模式

在DOM中,事件会按照事件冒泡或者事件捕获的方式进行传递和处理,当一个元素上触发了一个事件时,事件会首先经过捕获阶段,然后再经过目标阶段,最后是冒泡阶段。在这个过程中,每个元素都有机会处理事件,如果当前元素无法处理事件,事件会被传递给其父元素,直到document对象

4. 总结

4.1 设计优点

  • 开闭原则

    无需更改现行制度,添加新的审批人只需告诉前一级经理即可

  • 单一职责原则

    每一个经理只负责自己能处理的事情

4.2 适用场景

  • 请求会被多个处理者处理时,无论是否按照一定的顺序,都可以使用责任链模式

5. 参考资料

[1] 责任链设计模式(职责链模式) (refactoringguru.cn)

[2] 继承与原型链 - JavaScript | MDN (mozilla.org)