TIP
职责链模式的定义是: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他们。
# 案例:定金优惠券
某电商卖手机,用户可以先交定金,正式购买的时候会根据用户交的定金给用户返优惠券。支付过500元定金的用户会收到100元的优惠券,支付过200元定金的用户会收到50元的优惠券,没有支付过定金的不返优惠券,且在库存有限的情况下不一定保证能买到。
一般实现思路:根据用户支付的定金额度和库存做各种判断。虽然可以实现功能,但是代码难以阅读,而且维护起来很麻烦
职责链模式重构代码:
var order500 = function (orderType, pay, stock) {
if (orderType == 1 && pay) {
console.log("500元定金预购,得到100元优惠券");
} else {
return "nextSuccessor";
}
}
var order200 = function (orderType, pay, stock) {
if (orderType == 2 && pay) {
console.log("200元定金预购,得到50元优惠券");
} else {
return "nextSuccessor";
}
}
var orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log("普通购买,无优惠券");
} else {
console.log("库存不足");
}
}
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);
chainOrder500.passRequest(2, true, 500);
chainOrder500.passRequest(1, false, 500);
chainOrder500.passRequest(1, false, 0);
# 异步的职责链
节点中的处理是异步的,异步返回的结果决定请求是否流向下一个节点
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;
}
Chain.prototype.next=function(){
return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
//创建职责链的节点
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();
},2000);
})
var fn3=new Chain(function(){
console.log(3);
})
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest();
# 职责链模式的优缺点
优点
- 解耦了请求发送者和N个接收者之间的复杂关系
- 使用了职责链模式之后,链中的节点对象可以灵活的拆分重组。增加或者删除一个节点,或者改变节点在链中的位置都是轻而易举的事情。
缺点:
- 不能保证请求一定会被链中的节点处理
- 可能链中的大部分节点并没有起到实质性的作用,它们的作用仅仅是让请求传递下去,从性能方面考虑,我们要避免过长的职责链带来的性能损耗。
# 用AOP实现职责链
基于上面的代码,我们利用JavaScript的函数式特性,实现一种更方便的职责链模式
var order500 = function (orderType, pay, stock) {
if (orderType == 1 && pay) {
console.log("500元定金预购,得到100元优惠券");
} else {
return "nextSuccessor";
}
}
var order200 = function (orderType, pay, stock) {
if (orderType == 2 && pay) {
console.log("200元定金预购,得到50元优惠券");
} else {
return "nextSuccessor";
}
}
var orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log("普通购买,无优惠券");
} else {
console.log("库存不足");
}
}
Function.prototype.after = function (fn) {
var self = this;
return function () {
var ret = self.apply(this, arguments);
if (ret === "nextSuccessor") {
return fn.apply(this, arguments);
}
return ret;
}
}
var order = order500.after(order200).after(orderNormal);
//开始发起请求
order(1, true, 500);
order(2, true, 500);
order(1, false, 500);
order(1, false, 0);
# 小结
无论是作用域链、原型链,还是DOM节点中的事件冒泡,我们都能从中找到职责链模式的影子。
职责链模式可以很好地帮助我们管理代码,降低发起请求的对象和处理请求的对象之间的耦合性。职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。