职责链模式
2018-07-06 本文已影响0人
bby365
处理请求的对象被连接成一条链,发送者不知道接收是谁,弱化两者之间的强联系。
场景:乘坐公交车刷卡
- 电商定金用户购买商品流程
- 一般代码
使用if-else
语句,缺点:分支过多,后期维护困难。 - 优化一
将每一种情况,单独写成一个函数,如果处理不了,指定下一个处理函数去处理。
var order500 = function( orderType, pay, stock ){
if ( orderType === 1 && pay === true ){
console.log( '500 元定金预购, 得到100 优惠券' );
}else{
order200( orderType, pay, stock ); // 将请求传递给200 元订单
}
};
这样,每次都从order500开始,一直往下传递,直到被处理。
缺点:每个函数都要指定特定的下一个处理函数,这样的话,顺序固定,太僵硬。
- 优化2
每个处理函数,不再指定特定的处理函数,只是返回一个固定的标识符。
然后创建一个构造函数chain
,具有2个原型方法:指定下一个节点 和 传递请求。
// 1. 不会指定下一个节点,返回一个标识符,增加灵活性。
var order500 = function( orderType, pay, stock ){
if ( orderType === 1 && pay === true ){
console.log( '500 元定金预购,得到100 优惠券' );
}else{
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
// 2. 创建Chain构造函数,包括2个原型方法。
// 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;
};
// 3-1 包装成职责链的节点
var chainOrder500 = new Chain( order500 );
var chainOrder200 = new Chain( order200 );
var chainOrderNormal = new Chain( orderNormal );
// 3-2 指定下一个节点
chainOrder500.setNextSuccessor( chainOrder200 );
chainOrder200.setNextSuccessor( chainOrderNormal );
// 3-3 请求从第一个节点开始,处理不了,向后传递
chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
优点:
- 可以自由的组合,指定请求传递顺序。
- 有新的需求,添加也很简单,调用
setNextSuccessor()
可以轻松插入到指定位置。
- 异步职责链
上面的例子,返回一个特定的值来把请求传递个下一个节点。但是有异步请求的情况下,需要等待返回结果。这就需要节点,有权利可以决定什么时候传递请求。
模拟异步请求例子
var fn1 = new Chain(function(){
console.log(1)
return 'nextSuccessor'
})
var fn2 = new Chain(function(){
console.log(2)
var self = this;
setTimeout(function(){
return 'nextSuccessor'
},2000)
})
var fn3 = new Chain(function(){
console.log(3)
})
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3)
console.log(fn1.passRequest())
// 结果:1,2,undefined
分析:
- fn2 中有个异步方法,fn2.passRequest()返回的是undefined ,异步方法中的返回值没有作用。
- 如果想要执行fn3,那么fn2中的异步方法要调用一个原型方法去将请求传递给fn3
- 上述的方法都放到原型上是因为这些方法是公用的,每一个节点都可能调用。
改进:
// 增加一个原型方法
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)
console.log(fn1.passRequest())