JavaScript设计模式与开发实践(九)职责链模式

职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

实例

当我们使用京东或者淘宝买东西时,有些商品可能会根据是否付过定金以及定金的额度进行不同的处理,比如支付500定金的会赠送100元优惠券,200元赠送50优惠券等。转换成代码是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function userIdentity(orderType, pay, stock) {
if (orderType === 1) { // 500 元定金购买模式
if (pay === true) { // 已支付定金
console.log('500 元定金预购, 得到 100 优惠券');
} else { // 未支付定金,降级到普通购买模式
if (stock > 0) { // 用于普通购买的手机还有库存
console.log('普通购买, 无优惠券');
} else {
console.log('手机库存不足');
}
}
}
else if (orderType === 2) { // 200 元定金购买模式
if (pay === true) {
console.log('200 元定金预购, 得到 50 优惠券');
} else {
if (stock > 0) {
console.log('普通购买, 无优惠券');
} else {
console.log('手机库存不足');
}
}
}
else if (orderType === 3) {
if (stock > 0) {
console.log('普通购买, 无优惠券');
} else {
console.log('手机库存不足');
}
}
}

userIdentity(1, true, 500);

这样写的坏处是嵌套了很多的if else语句,此外如果想要加入新的逻辑(比如新增300元的优惠券)会很麻烦。我们可以借助职责链模式来帮助我们简化代码:

  • 首先将500,200,正常购买三个逻辑各自封装成函数,约定如果当前函数无法处理就返回 nextSuccessor
  • 新建一个构造函数,用**passRequset接收参数处理逻辑,setNextSuccessor**指定当前函数无法处理时的下一个函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const order500 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) {
console.log('500 元定金预购,得到 100 优惠券');
} else {
return 'nextSuccessor';
}
};
const order200 = function (orderType, pay, stock) {
if (orderType === 2 && pay === true) {
console.log('200 元定金预购,得到 50 优惠券');
} else {
return 'nextSuccessor';
}
};
const orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('手机库存不足');
}
};

class Chain {
constructor(fn) {
this.fn = fn
this.successor = null
}
setNextSuccessor(successor) {
return this.successor = successor
}
passRequest() {
const ret = this.fn.apply(this, arguments);
if (ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor, arguments);
}
return ret;
}
next() {
return this.successor && this.successor.passRequest.apply(this, arguments)
}
}

const chain500 = new Chain(order500)
const chain200 = new Chain(order200)
const chainNormal = new Chain(orderNormal)
chain500.setNextSuccessor(chain200).setNextSuccessor(chainNormal)

chain500.passRequest(3,false,0)

如果不是同步的情况,例如当我们用职责链来处理异步逻辑,就不能直接在业务函数中直接返回**nextSuccessor而需要调用 next**进行下一步。

AOP实现职责链

但是AOP实现主要捋清楚各个this指向的关系,这点很重要。

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.after = function (fn) {
const _this = this
return function () {
const res = _this.apply(this, arguments)
if (res === 'nextSuccessor') {
return fn.apply(this, arguments)
}
return res
}
}
const order = order500.after(order200).after(orderNormal)
order(1, true, 500);

总结

职责链模式可以很好地帮助我们管理代码,降低发起请求的对象和处理请求的对象之间的耦合性。

职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。无论是作用域链、原型链,还是DOM节点中的事件冒泡,我们都能从中找到职责链模式的影子。职责链模式还可以和组合模式结合在一起,用来连接部件和父部件,或是提高组合对象的效率。

JavaScript设计模式与开发实践(九)职责链模式
https://jing-jiu.github.io/jing-jiu/2023/01/14/notebooks/JavaScript设计模式与实践/设计模式(九)/
作者
Jing-Jiu
发布于
2023年1月14日
许可协议