ToC
假设项目中有这样一个需求:
- orderType 参数的值有三种情况
- === 1 时表示 500 定金
- === 2 时表示 200 定金
- === 3 是表示 无优惠券,原价购买
- pay 表示是否支付定金
- stack 表示还有多少库存
那么根据需求大概可以写出这样的代码:
1function order(orderType, pay, stack) {2 if (orderType === 1) { // 500 定金3 if (pay === true) {4 console.log('支付 500 定金成功')5 } else {6 if (stack > 0) {7 console.log('普通订单, 无优惠券')8 } else {9 console.log('库存不足')10 }23 collapsed lines
11 }12 } else if (orderType === 2) { // 200 定金13 if (pay === true) {14 console.log('支付 200 定金成功')15 } else {16 if (stack > 0) {17 console.log('普通订单, 无优惠券')18 } else {19 console.log('库存不足')20 }21 }22 } else if (orderType === 3) {23 if (stack > 0) {24 console.log('普通订单, 无优惠券')25 } else {26 console.log('库存不足')27 }28 }29}30
31order(1, true, 10)32order(2, false, 10)33order(3, true, 0)
这么做确实可以完成需求, 但是代码并不好看, 同时层级嵌套也比较深, 用了大量的 if/else. 根据这种场景就引申除了这次的主题: 责任链模式.
责任链模式 : 每个函数只处理一种场景的情况(类似单一职责), 如果不符合条件则转交个下一个方法处理, 这种串联的关系就是责任链模式.
根据责任链原则, 可以得出第一版代码:
1function order500(orderType, pay, stack) {2 if (orderType === 1 && pay === true) {3 console.log('支付 500 定金成功')4 } else {5 // orderNormal(orderType, pay, stack)6 return 'next'7 }8}9
10function order200(orderType, pay, stack) {48 collapsed lines
11 if (orderType === 2 && pay === true) {12 console.log('支付 200 定金成功')13 } else {14 // orderNormal(orderType, pay, stack)15 return 'next'16 }17}18
19function orderNormal(orderType, pay, stack) {20 if (stack > 0) {21 console.log('普通订单, 无优惠券')22 } else {23 console.log('库存不足')24 }25}26
27function Chain(fn) {28 this.fn = fn29 this.next = null30}31
32Chain.prototype.set = function (fn) {33 this.next = fn34}35
36Chain.prototype.run = function () {37 const result = this.fn.apply(this, arguments)38
39 // 如果得到的返回值是 next, 则转交下一个方法处理40 if (result === 'next') {41 // 如果没有下一个方法, 则会异常42 return this.next && this.next.run.apply(this.next, arguments)43 }44}45
46// 包装函数, 保证每次调用都会返回一个新的 Chain 对象47var chainOrder500 = new Chain(order500)48var chainOrder200 = new Chain(order200)49var chainOrderNormal = new Chain(orderNormal)50
51// 设定链条关系52chainOrder500.set(chainOrder200)53chainOrder200.set(chainOrderNormal)54
55// 运行这个链条56chainOrder500.run(1, true, 10)57chainOrder500.run(2, false, 10)58chainOrder500.run(3, true, 0)
但这种方式还是比较麻烦, 需要对每一个方法进行包装, 可以在 Function
的原型上添加额外的方法, 使用链式调用的方式来优化这个问题.
1Function.prototype.after = function (fn) {2 const self = this3 return function () {4 const result = self.apply(this, arguments)5
6 return result === 'next'7 ? fn.apply(this, arguments)8 : result9 }10}6 collapsed lines
11
12var order = order500.after(order200).after(orderNormal)13
14order(1, true, 10)15order(2, false, 10)16order(3, true, 0)
这么做确实解决了问题, 但是日常开发中不应该去给内置构造函数添加方法, 因为这会导致后续接手代码的人很困惑, “为什么这个函数可以直接调用方法, 却没看到定义?”, 所以我们可以自己定义一个构造函数, 然后在原型上添加方法, 最后用实例化后的对象来操作:
1function Chain2() {2 this.chain = []3}4
5Chain2.prototype.add = function (fn) {6 this.chain.push(fn)7 return this8}9
10Chain2.prototype.run = function () {21 collapsed lines
11 const self = this12 const args = arguments13
14 for (let i = 0; i < this.chain.length; i++) {15 const result = this.chain[i].apply(this, args)16
17 if (result === 'next') {18 continue19 } else {20 return result21 }22 }23}24
25const chain = new Chain2()26
27chain.add(order500).add(order200).add(orderNormal)28
29chain.run(1, true, 10)30chain.run(2, false, 10)31chain.run(3, true, 0)
以上.