职责链模式
原文链接 https://ronghuaxueleng.github.io/2016/10/12/JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5-15%E8%81%8C%E8%B4%A3%E9%93%BE%E6%A8%A1%E5%BC%8F/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象都练成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
如图所示:
请求→A→B→C→D
再来一个更形象的比喻,读书的时候假设你坐在前面,要在上课时告诉后排一些事情,这时候你可能会选择写一张小纸条,小纸条就会陆续的被向后传递。
从上面的例子中,我们可以看到职责链的优点:请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。
实际开发中的职责链模式
需求:一个售卖手机的电商网站,交纳500定金和200定金和没交定金的有不同的优惠状况。
我们刚开始可能会这么写:
// orderType: 订单类型(普通用户或定金用户),1为500元定金用户,2为200,3为普通用户
// pay:表示是否已支付定金,true或false
// 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) {
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);
以上代码虽然保证了可以用的状态,但是只是能用,如果要修改就会很麻烦,而且阅读体验也不是很好,所以我们做一点优化
var order500 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) { //500元定金购买模式
console.log('500元定金预定,已获得100元优惠券');
} else {
order200 (orderType, pay, stock); //将请求传递给200元订单
}
};
var order200 = function (orderType, pay, stock) {
if (orderType === 2 && pay === true) { //200元定金购买模式
console.log('200元定金预定,已获得50元优惠券');
} else {
orderNormal (orderType, pay, stock); //将请求传递给普通订单
}
};
var orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('库存不足');
}
};
order500 (1, true, 500);
虽然这有了小小进步,不过请求在链条中的传递依然很僵硬,传递请求的代码被耦合在了业务函数中,这显然是违反开放-封闭原则的,例如如果有一天我们要增加300元预定或者200元预定,意味着就必须改动这些业务函数的内部,就像一根根环环相扣的死结链条。
灵活可拆分的职责链
var order500 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) { //500元定金购买模式
console.log('500元定金预定,已获得100元优惠券');
} else {
return 'nextSuccessor'; //我不知道下一个节点是谁,反正向后传递就好了
}
};
var order200 = function (orderType, pay, stock) {
if (orderType === 2 && pay === true) { //200元定金购买模式
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(successor) {
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.passRequest(1, true, 500); //500定金优惠100
chainOrder200.passRequest(2, true, 500); //200定金优惠50
chainOrderNormal.passRequest(3, true, 500); //普通购买无优惠券
chainOrderNormal.passRequest(1, false, 0); //库存不足