《javascript设计模式与开发实践》阅读笔记(13)—— 职责链模式

 

职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。es6

 

书里的订单的例子

假设咱们负责一个售卖手机的电商网站,通过分别交纳500元定金和200元定金的两轮预约(订单已在此时生成),如今已经到了正式购买的阶段。公司针对支付过定金的用户有必定的优惠政策。在正式购买后,已经支付过500元定金的用户会收到100元的商城优惠券,200元定金的用户能够收到50元的优惠券,而以前没有支付定金的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的状况下不必定保证能买到。ajax

orderType:表示订单类型(定金用户或者普通购买用户),值为1的时候是500元定金用户,为2的时候是200元定金用户,为3的时候是普通购买用户。
pay:表示用户是否已经支付定金,值为true表示已付钱,若是没有支付定金,只能降级进入普通购买模式。
num:表示当前用于普通购买的手机库存数量,已经支付过500元或者200元定金的用户不受此限制。闭包

 1     var order=function( orderType, pay, num ){  2         if ( orderType === 1 ){        // 500元定金购买模式
 3             if ( pay === true ){              // 已支付定金
 4                 console.log( '500元定金预购, 获得100优惠券' );  5             }else{                            // 未支付定金,降级到普通购买模式
 6                 if ( num > 0 ){                      // 用于普通购买的手机还有库存
 7                     console.log( '普通购买, 无优惠券' );  8                 }else{  9                     console.log( '手机库存不足' ); 10  } 11  } 12         }else if ( orderType === 2 ){  // 200元定金购买模式
13             if ( pay === true ){ 14                 console.log( '200元定金预购, 获得50优惠券' ); 15             }else{ 16                 if ( num > 0 ){ 17                     console.log( '普通购买, 无优惠券' ); 18                 }else{ 19                     console.log( '手机库存不足' ); 20  } 21  } 22         }else if ( orderType === 3 ){   //普通购买
23             if ( num > 0 ){ 24                 console.log( '普通购买, 无优惠券' ); 25             }else{ 26                 console.log( '手机库存不足' ); 27  } 28  } 29  } 30 
31     order( 1 , true, 500); // 500元定金预购, 获得100优惠券

 

上面的代码其实不用看完就已经能感觉到十分的繁琐,把全部的逻辑都列出来使得函数变得十分臃肿。这种相似的代码在策略模式中其实出现过,最后使用策略模式很好的消化了内部的条件分支,这里的话咱们用职责链模式也能解决。app

 

使用职责链的思想重写函数

思路:彼此委托,我能够执行我就执行,我执行不了我就委托给别人执行,因此咱们把500订单的执行,200订单的执行和普通购买的执行分别写成一个函数。异步

 1     var order500=function( orderType, pay, num ){  2         if ( orderType === 1 && pay === true ){    //若是是500订单且已经付款
 3             console.log( '500元定金预购, 获得100优惠券' );  4         }else{  5             order200( orderType, pay, num );      //将请求传递给200 元订单
 6  }  7  };  8 
 9     var order200=function( orderType, pay, num ){ 10         if ( orderType === 2 && pay === true ){    //若是是200订单且已经付款
11             console.log( '200元定金预购, 获得50优惠券' ); 12         }else{ 13             orderNormal( orderType, pay, num );  //将请求传递给普通订单
14  } 15  }; 16 
17     var orderNormal=function( orderType, pay, num ){ 18         if ( num > 0 ){             //普通购买,若是数量足够
19             console.log( '普通购买, 无优惠券' ); 20         }else{ 21             console.log( '手机库存不足' ); 22  } 23  } 24 
25     order500( 1 , true, 500 );   //500元定金预购, 获得100优惠券
26     order500( 1, false, 500 );   //普通购买, 无优惠券
27     order500( 2 , true, 500 );   //200元定金预购, 获得50优惠券

 

这个函数比以前的已经好了不少,可是仍是有些缺陷,假设说咱们增添了300元的订单,那么咱们就要改写上面的函数。函数

能够说,只要有点改动,不管是增长仍是删除哪一环,咱们必须拆开这个链条才行,并且必须修改函数内部。性能

那这个问题的根源在哪里,传递请求是职责链模式的根本,也是这个函数和上一个函数最大的区别所在,因此传递请求这一点是没有问题的,咱们须要他们一个委托一个,那么问题的所在就是委托函数之间耦合的太死,以致于改动必须深刻内部才行。因此,咱们要想办法下降委托函数彼此间的耦合。网站

 

进一步修改函数

思路:想要解耦合,那么就是用变量代替具体的函数,而后用一个对象安排每一个函数以后的委托函数。可是每一个函数后面的委托函数是不同的,因此不能使用相同的变量,可是若是使用不一样的变量,其实质和如今并无多少区别。this

从委托这个字眼,咱们想到对象之间能够很方便的调用方法,对象接收这个函数,并产生相应的属性,包括当前的函数,和以后须要执行的委托函数。仔细一想,咱们的作法其实至关于扩展原来的函数,在不改动原来函数的基础上,咱们想要实现委托,那就只有把函数变成参数来生成一个更为全能的对象,而后让这几个功能更强的对象之间彼此交互。由于这些对象彼此相似,功能也相同,因此咱们须要一个模板来生成这些对象。同时为了让对象能够意识到该委托别的对象了,函数的返回值应该作一个统一规定。spa

 1     /**首先是具体的功能函数**/
 2     var order500=function( orderType, pay, num ){  3         if ( orderType === 1 && pay === true ){    //若是是500订单且已经付款
 4             console.log( '500元定金预购, 获得100优惠券' );  5         }else{  6             return "next"    //统一规定的执行结果
 7  }  8  };  9 
10     var order200=function( orderType, pay, num ){ 11         if ( orderType === 2 && pay === true ){    //若是是200订单且已经付款
12             console.log( '200元定金预购, 获得50优惠券' ); 13         }else{ 14             return "next"
15  } 16  }; 17 
18     var orderNormal=function( orderType, pay, num ){ 19         if ( num > 0 ){             //普通购买,若是数量足够
20             console.log( '普通购买, 无优惠券' ); 21         }else{ 22             console.log( '手机库存不足' ); 23  } 24  } 25 
26     /**闭包建立一个类,实例职责对象,参数就是上面的功能函数**/
27     var order=(function(){ 28         var constructor=function( fn ){  //构造器,存储当前的函数和后面委托的对象
29             this.fn=fn; 30             this.next=null; 31  } 32         constructor.prototype.setnext=function( nextobj ){ //设定委托的对象
33             return this.next=nextobj; 34  } 35         constructor.prototype.do=function(){  //执行函数
36             var result=this.fn.apply(this,arguments);  //得到执行结果
37 
38             if( result==="next" ){  //当执行结果为规定值时
39                 return this.next.do.apply(this.next,arguments);  //委托对象执行函数
40  } 41             return result; 42  } 43 
44         return constructor; 45  })(); 46 
47     /**实例过程**/
48     var order_500=new order( order500 ); 49     var order_200=new order( order200 ); 50     var order_normal=new order( orderNormal ); 51 
52     /**职责对象间设定委托的链条关系**/
53  order_500.setnext( order_200 ); 54  order_200.setnext( order_normal ); 55 
56     /**初始调用的接口**/
57     /**这里其实能够再包装一个固定的接口,这样无论链条究竟从哪里开始,咱们也不须要改变初始的调用函数**/
58     order_500.do( 2, true, 500 );  //200元定金预购, 获得50优惠券

 

这时候若是加了300元的订单也很方便

1     var order_300=new ( order300 );
2     order_500.setnext( order_300 );
3     order_300.setnext( order_200 );

 

小结:咱们把手动的修改,变成了经过对象控制,这样只须要几个句子就能很方便的修改业务逻辑,不用去改动任何源码。

 

用es6的类来完成

 1  class order{  2  constructor( fn ){  3             this.fn=fn;  4             this.next=null;  5  }  6  setnext( nextobj ){  7             return this.next=nextobj;  8  }  9         do(){ 10             var result=this.fn.apply(this,arguments);  //得到执行结果
11 
12             if( result==="next" ){  //当执行结果为规定值时
13                 return this.next.do.apply(this.next,arguments);  //委托对象执行函数
14  } 15             return result; 16  } 17     }

 

异步职责链

上面的代码咱们是约定好了一个返回值,并把这个返回值写死在了原型方法上,这样咱们能够直接获得最终答案,但实际上不少时候,可能咱们函数的执行须要等待一个ajax的响应,这种时候时间上是不一样步的,并且返回的执行结果咱们也不能保证一致,因此咱们能够添加一个方法,用来手动触发下一个委托函数。

 1     constructor.prototype.nextfun= function(){  2         return this.next && this.next.do.apply( this.next, arguments ); //若是next存在 就执行它的方法  3  };  4 
 5     var fn1 = new order(function(){  6         console.log( 1 );  7         return 'next';  8  });  9     var fn2 = new order(function(){ 10         console.log( 2 ); 11         var self = this; 12         setTimeout(function(){ 13  self.nextfun(); 14         }, 1000 ); 15  }); 16 
17     var fn3 = new order(function(){ 18         console.log( 3 ); 19  }); 20     fn1.setnext( fn2 ).setnext( fn3 );  //链式调用,由于setnext方法返回了对象
21     fn1.do();  //先出现1,2 隔了一秒以后出现3

 

职责链模式的优缺点

优势如咱们所见,原本有不少等待执行的函数,咱们不知道哪个能够执行请求的时候就要一个一个去验证,因而出现了最开始的那个代码。而在职责链模式中,因为不知道链中的哪一个节点能够处理你发出的请求,因此你只需把请求传递给第一个节点便可。

使用了职责链模式以后,链中的节点对象能够灵活地拆分重组。增长或者删除一个节点,或者改变节点在链中的位置都是垂手可得的事情。这一点咱们也已经看到,在上面的例子中,增长一种订单彻底不须要改动其余订单函数中的代码。

并且职责链模式还有一个优势,那就是能够手动指定起始节点,请求并非非得从链中的第一个节点开始传递,当咱们明确第一个节点并不具备执行能力时,咱们能够从第二个或者更后面的节点开始传递,这样能够减小请求在链里面传递的次数。

 

缺点,可能没有一个节点能够执行,请求会从链尾离开或者抛出一个错误。并且为了职责链而职责链可能由于过多没有必要的节点,带来性能方面的问题。

 

总结

职责链模式在js开发很容易被忽略,它结合不管是结合组合模式仍是利用AOP的思想,都能发挥巨大做用。

相关文章
相关标签/搜索