本文已参与「新人创作礼」活动,一起开启掘金创作之路。
关键词: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 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
应用场景
主要应用于解耦发起请求的对象和处理请求的对象,更好的帮助我们管理代码。针对不同场景处理请求。就比如优惠券,发帖的审核等。
参考资料: