早高峰时,公交车上人实在太多了,经常上车后找不到售票员在哪,所以只好把两块钱硬币往前面递。除非你运气够好,站在你前面的第一个人就是售票员,否则,你的硬币通常要在 N 个人手上传递,才能最终到达售票员的手里。这种通过传递,最终传到售票员的模式就是职责链模式。
职责链模式(Chain of Responsibility Pattern):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。
现在我们做一个预售付定金,正式购买时返优惠券的功能。定金500对应100优惠券,200对应50优惠券,未付定金原价购买。
实现流程如下:
/***** orderType:预付类型 pay:是否付款 stock:商品库存 *****/
var 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 优惠券
虽然实现了功能,但是太多的if判断失去了代码的阅读性,如果在增加定金类型会不断堆砌判断,我们也需要增加可维护性,利用职责链的思想给它解耦。
var order500 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) {
console.log('500 元定金预购,得到 100 优惠券');
} else {
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var order200 = function (orderType, pay, stock) {
if (orderType === 2 && pay === true) {
console.log('200 元定金预购,得到 50 优惠券');
} else {
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('手机库存不足');
}
};
// Chain.prototype.setNextSuccessor 指定在链中的下一个节点
// Chain.prototype.passRequest 传递请求给某个节点
var Chain = function (fn) {
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function (successor) {
return this.successor = successor;
};
Chain.prototype.passRequest = function () {
var ret = this.fn.apply(this, arguments);
if (ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor, arguments);
}
return ret;
};
//包装成职责链的节点
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var 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返80优惠券类型,我们只需添加一个函数,放入职责链中执行。
var order300 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) {
console.log('300 元定金预购,得到 80 优惠券');
} else {
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
chainOrder300= new Chain( order300 );
chainOrder500.setNextSuccessor( chainOrder300);
chainOrder300.setNextSuccessor( chainOrder200);
上面的例子中我们模拟的是同步事件,开发中我们也会遇到一些异步的问题,比如ajax请求等等。我们模拟一个职责链异步实现。
var fn1 = new Chain(function () {
console.log(1);
return 'nextSuccessor';
});
var fn2 = new Chain(function () {
console.log(2);
var self = this;
setTimeout(function () {
self.next();
}, 1000);
});
var fn3 = new Chain(function () {
console.log(3);
});
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest();
现在我们得到了一个特殊的链条,请求在链中的节点里传递,但节点有权利决定什么时候把请求交给下一个节点。
职责链模式可以很好地帮助我们管理代码,降低发起请求的对象和处理请求的对象之间的耦合性。职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。无论是作用域链、原型链,还是 DOM 节点中的事件冒泡,我们都能从中找到职责链模式的影子。职责链模式还可以和组合模式结合在一起,用来连接部件和父部件,或是提高组合对象的效率。