职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
有这样一个需求:
假设我们负责一个售卖手机的电商网站,经过分别交纳500元定金和200元定金的两轮预定后(订单已在此时生成),现在已经到了正式购买的阶段。
公司针对支付过定金的用户有一定的优惠政策。在正式购买后,已经支付过500元定金的用户会收到100元的商城优惠券,200元定金的用户可以收到50元的优惠券,而之前没有支付定金的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。
这个需求写成代码就是这样:
const order = function (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('手机库存不足');
}
}
};
order(1, true, 500); // 输出: 500元定金预购, 得到100优惠券
这样的代码有下面的这些问题:
- 大量重复代码
- 所有逻辑都写在了一个大函数里面,维护起来很困难
- 耦合性太强,加入某天需求变更,比如新增一个300元的优惠策略,那么几乎要改动整块代码
使用职责链模式重写
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('手机库存不足')
}
}
const Chain = function (fn) {
this.fn = fn
this.successor = null
}
Chain.prototype.setNextSuccessor = function (successor) {
return this.successor = successor
}
Chain.prototype.passRequest = function () {
let ret = this.fn.apply(this, arguments)
if (ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor, arguments)
}
return ret
}
const chainOrder500 = new Chain(order500)
const chainOrder200 = new Chain(order200)
const chainOrderNormal = new Chain(orderNormal)
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder200.setNextSuccessor(chainOrderNormal)
chainOrder500.passRequest( 1, true, 500 ); // 输出:500元定金预购,得到100优惠券
chainOrder500.passRequest( 2, true, 500 ); // 输出:200元定金预购,得到50优惠券
chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
这样子,就把各个阶段的逻辑解耦开了,如果需要增加300元的优惠策略,也比较容易:
var order300 = function(){
// 300价位订单的处理逻辑
};
chainOrder300= new Chain( order300 );
chainOrder500.setNextSuccessor( chainOrder300);
chainOrder300.setNextSuccessor( chainOrder200);
利用js的高级函数,还能更加简洁方便的创建职责链:
/* 用AOP实现职责链模式 */
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
}
}
let order=order500.after(order200).after(orderNormal)
order( 1, true, 500 ); // 输出:500元定金预购,得到100优惠券
order( 2, true, 500 ); // 输出:200元定金预购,得到50优惠券
order( 1, false, 500 ); // 输出:普通购买,无优惠券
用AOP来实现职责链既简单又巧妙,但这种把函数叠在一起的方式,同时也叠加了函数的作用域,如果链条太长的话,也会对性能有较大的影响。
我的理解:职责链模式算是迭代器模式的一个延伸,里面的思想与迭代器模式有些相似之处。迭代器是把队列每个元素作为输入,然后用户自己定义处理逻辑,而职责链是用户指定多个处理逻辑,处理同一个输入,只有满足条件的那个处理逻辑才能真正输出,而其他处理逻辑只是把输入传递给下一个处理逻辑。