本文已参与「新人创作礼」活动,一起开启掘金创作之路。
关键词:Command ChainOfResponsibility
责任链模式在我们平时开发过程中随处可见,比如作用域链、原型链、事件冒泡等。
什么是原型链
__proto__?
为什么这么设计?
JS 为了实现面向对象编程的一种设计,基于原型链可以让 JS 对象拥有封装继承多态等面向对象的特性。
什么是事件冒泡?
事件冒泡:交互事件发生后,从最小元素 div 开始传播,传到顶层 document。
为什么这么设计?
最小元素上可能没有绑定 onclick 事件,所以得层层传递,传到绑定了事件函数的元素上。
换句话说:事件源本身(可能)没有处理事件的能力。
解决什么问题
上述两个例子简单介绍了下职责链模式的经典用例,类比到日常其实就像我们上学的时候上课传纸条、玩击鼓传花,纸条 谁是赵日天?,从第一排传下去,如果不是就不会应答,继续往后传递。
先来看两段珍贵无比的代码,走进职责链模式的内心世界:
业务逻辑集合递减
需求:
- 已生成订单,定金 500,商城优惠券 100元
- 已生成订单,定金 200,商城优惠券 50 元
- 正常购买,是否有库存,原价购买
- 下了单没付钱的一律按照原价购买
改造前:
/**
* orderType: 1 -> 500定金 2 -> 200定金 3 -> 普通购买
* isPaid -> 是否已支付定金
* stock -> 库存
*/
function getOrder(orderType, isPaid, stock) {
// 500元定金购买模式
if (orderType === 1) {
// 已支付定金
if (isPaid === true) {
console.log('500元定金预购,得到100优惠券');
} else { // 未支付定金,降级到普通购买模式
// 用于普通购买的手机还有库存
if (stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('库存不足');
}
}
return
}
// 200元定金购买模式
if (orderType === 2) {
if (pay === true) {
console.log('200元定金预购, 得到50优惠券');
} else {
if (stock > 0) {
console.log('普通购买, 无优惠券');
} else {
console.log('库存不足');
}
}
return
}
if (orderType === 3) {
if (stock > 0) {
console.log('普通购买, 无优惠券');
} else {
console.log('库存不足');
}
}
}
getOrder(1, true, 500); // 500元定金预购,得到100优惠券
问题:代码冗余,大量重复的业务逻辑,难以维护。用职责链模式对代码进行改造。
改造后:
// 定义职责链上的各个节点
const orderFor500 = (orderType, isPaid, stock) => {
if (orderType === 1 && isPaid) {
console.log('500元定金预购 得到100优惠券')
} else {
return 'nextTick'
}
}
const orderFor200 = (orderType, isPaid, stock) => {
if (orderType === 2 && isPaid) {
console.log('200元定金预购 得到500优惠券')
} else {
return 'nextTick'
}
}
const orderForNormal = (orderType, isPaid, stock) => {
if (stock > 0) {
console.log('普通购买, 无优惠券')
} else {
console.log('库存不足')
}
}
// 定义职责链
class Chain {
constructor(fn) {
this.fn = fn
this.nextTick = null
}
setNextTick(nextTick) {
return this.nextTick = nextTick
}
permit() {
const res = this.fn.apply(this, arguments);
if (res === 'nextTick') {
return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments)
}
return res
}
}
测试代码:
// 声明节点
const chainOrder500 = new Chain(orderFor500);
const chainOrder200 = new Chain(orderFor200);
const chainOrderNormal = new Chain(orderForNormal);
// 指定顺序
chainOrder500.setNextTick(chainOrder200)
chainOrder200.setNextTick(chainOrderNormal)
// 测试
chainOrder500.permit(1, true, 500)
chainOrder500.permit(2, true, 500)
chainOrder500.permit(3, true, 500)
chainOrder500.permit(1, false, 0)
// 500元定金预购 得到100优惠券
// 200元定金预购 得到500优惠券
// 普通购买, 无优惠券
// 库存不足
这样的写法也便于以后拓展,比如运营同学说:350 - 75
const orderFor350 = (orderType, isPaid, stock) => {
// code
}
const chainOrder350 = new Chain(orderFor350);
chainOrder500.setNextTick(chainOrder350)
chainOrder350.setNextTick(chainOrder200)
改造职责链使其能够异步
在节点函数中发起异步请求,异步请求返回的结果决定是否继续在职责链中 permit。
这时候让节点函数同步返回 nextTick 就没有意义了,所以 Chain 类需要增加方法 $next,手动传递请求给职责链中的下一个节点。
// 定义职责链
class Chain {
constructor(fn) {
this.fn = fn
this.nextTick = null
}
$next() {
return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments);
}
setNextTick(nextTick) {
return this.nextTick = nextTick
}
permit() {
const res = this.fn.apply(this, arguments);
if (res === 'nextTick') {
return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments)
}
return res
}
}
// 定义节点
const node1 = new Chain(function () {
showResult('node-1')
return 'nextTick'
})
const node2 = new Chain(function () {
showResult('node-2')
const _this = this;
const timer = setTimeout(() => {
_this.$next()
clearTimeout(timer)
}, 1000)
})
const node3 = new Chain(function () {
showResult('node-3')
})
// 测试代码
node1.setNextTick(node2).setNextTick(node3)
node1.permit()
请求在链中的节点里传递,节点则确定什么时候把请求交给下一个节点。
定义
在 GoF 给出的定义: 在职责链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
应用场景
主要应用于解耦发起请求的对象和处理请求的对象,更好的帮助我们管理代码。针对不同场景处理请求。就比如优惠券,发帖的审核等。
参考资料: