使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
优点
缺点
职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分节点并没有起到实质性的作用,它们的作用仅仅是让请求传递下去,从性能方面考虑,我们要避免过长的职责链带来的性能损耗。
来用一个demo
理解,职责链的目的与实现:
场景:某电商针对已付过定金的用户有优惠政策,在正式购买后,已经支付过 500 元定金的用户会收到 100 元的优惠券,200 元定金的用户可以收到 50 元优惠券,没有支付过定金的用户只能正常购买。
// orderType:订单类型// pay:是否已支付定金// stock:手机库存数量// 500元订单const order = (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) {if (pay === true) {console.log('200元定金预购,得到100优惠券')} else {if (stock > 0) {console.log('普通购买,无优惠券')} else {console.log('手机库存不足')}}}else if (orderType === 3) {if (stock > 0) {console.log('普通购买,无优惠券')} else {console.log('手机库存不足')}}}order(1, true, 500) // 500元定金预购,得到100优惠券
order
函数不仅巨大到难以阅读,而且需要经常进行修改。
// 500元订单const order500 = (orderType, pay, stock) => {if (orderType === 1 && pay == true) {console.log('500元定金预购,得到100优惠券')} else {order200(orderType, pay, stock)}}// 200元订单const order200 = (orderType, pay, stock) => {if (orderType === 2 && pay == true) {console.log('200元定金预购,得到100优惠券')} else {orderNormal(orderType, pay, stock)}}// 普通购买const orderNormal = (orderType, pay, stock) => {if (orderType === 3 && stock > 0) {console.log('普通购买,无优惠券')} else {console.log('手机库存不足')}}// 测试结果order500(1, true, 500) // 500元定金预购,得到100优惠券order500(1, false, 500) // 普通购买,无优惠券order500(2, true, 500) // 200元定金预购,得到100优惠券order500(3, false, 500) // 普通购买,无优惠券order500(3, false, 0) // 手机库存不足
改造后可以发现代码相对清晰了,但是链路代码和业务代码依然耦合在一起,进一步优化:
const order500 = (orderType, pay, stock) => {if (orderType === 1 && pay == true) {console.log('500元定金预购,得到100优惠券')} else {return 'nextSuccessor' // 下一个节点}}// 200元订单const order200 = (orderType, pay, stock) => {if (orderType === 2 && pay == true) {console.log('200元定金预购,得到100优惠券')} else {return 'nextSuccessor'}}// 普通购买const orderNormal = (orderType, pay, stock) => {if (orderType === 3 && stock > 0) {console.log('普通购买,无优惠券')} else {console.log('手机库存不足')}}// 具体相当于链表class Chain {constructor(fn) {this.fn = fnthis.successor = null}// 指定链中的下一个节点setNext = function(successor) {return this.successor = successor}// 返回结果或进入下一个节点init = function() {const ret = this.fn.apply(null, arguments)if (ret === 'nextSuccessor') {return this.successor && this.successor.init.apply(this.successor, arguments)}return ret}}// 把订单函数包装成职责链的节点const chainOrder500 = new Chain(order500)const chainOrder200 = new Chain(order200)const chainOrderNormal = new Chain(orderNormal)// 指定节点在职责链中的顺序chainOrder500.setNext(chainOrder200)chainOrder200.setNext(chainOrderNormal)// 最后,只需调用第一个节点chainOrder500.init(1, true, 500) // 500元定金预购,得到100优惠券chainOrder500.init(1, false, 500) // 普通购买,无优惠券chainOrder500.init(2, true, 500) // 200元定金预购,得到100优惠券chainOrder500.init(3, false, 500) // 普通购买,无优惠券chainOrder500.init(3, false, 0) // 手机库存不足
其实就是一个链表,往下一级一级走,直到目标一致的节点。
重构后,链路代码和业务代码彻底地分离。假如未来需要新增 order300
,那只需新增与其相关的函数而不必改动原有业务代码。
const order300 = () => {// ...}const chainOrder300 = new Chain(order300)chainOrder500.setNext(chainOrder300)chainOrder300.setNext(chainOrder200)
在之前的职责链实现中,我们利用了一个Chain
类来把普通函数包装成职责链的节点。
其实是利用JavaScript的函数式特性,有一种更加方便的方法来创建职责链:
const order500 = (orderType, pay, stock) => {if (orderType === 1 && pay == true) {console.log('500元定金预购,得到100优惠券')} else {return 'nextSuccessor' // 下一个节点}}const order200 = (orderType, pay, stock) => {if (orderType === 2 && pay == true) {console.log('200元定金预购,得到100优惠券')} else {return 'nextSuccessor'}}const orderNormal = (orderType, pay, stock) => {if (orderType === 3 && stock > 0) {console.log('普通购买,无优惠券')} else {console.log('手机库存不足')}}// 函数原型增加方法Function.prototype.after = function(fn){let self = this;return function() {let ret = self.apply(this, arguments)if (ret === 'nextSuccessor') {return fn.apply(this, arguments)}return ret}}const order = order500.after(order200).after(orderNormal)order(1, true, 500) // 500元定金预购,得到100优惠券order(1, false, 500) // 普通购买,无优惠券order(2, true, 500) // 200元定金预购,得到100优惠券order(3, false, 500) // 普通购买,无优惠券order(3, false, 0) // 手机库存不足
用AOP来实现职责链既简单又巧妙,但这种把函数叠在一起的方式,同时也叠加了函数的作用域。
如果链条太长的话,也会对性能有较大的影响。