javascript设计模式学习之十三——职责链模式
一、职责链的定义和使用场景
职责链模式的定义是,职责链模式将一系列可能会处理请求的对象连接成一条链,请求在这些对象之间一次传递,直到遇到一个可以处理它的对象。从而避免请求的发送者和接收者之间的耦合关系。
职责链模式的优点是:请求发送者只需要直到链中的第一个节点,从而解耦了发送者和一组接收者之间的强联系。此外,使用了职责链模式之后,链中的节点对象可以灵活地拆分重组,增加或者删除 一个节点,以及改变节点在链中的位置都是轻而易举的。
职责链模式的缺点是:首先不能保证某个请求一定会被链中的某个节点处理,这种情况系下可以在链尾增加一个保底的接受者节点来处理这种即将离开链尾的请求。其次,职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分的节点并没有起到实质性的作用,从性能的角度考虑,应当避免过长的职责链带来的性能损耗。
无论是作用域链,原型链,还是DOM节点中的事件冒泡,从中都可以找到职责链模式的影子。
结合自己开发经验,在轻量级MVVM的实现中,就体现了职责链的设计模式。
1.具有指向下一个节点的属性:this.parent
2.在具体的事件处理函数中进行递归调用,以便事件沿着职责链的方向传递(冒泡?)
二、职责链模式应用案例
假设有这么一种场景:一个售卖手机的电商网站,经过分别交纳500元定金和200元定金的两轮预订之后(订单在此时已经生成),现在进入了正式购买阶段。
公司针对支付过定金的客户有一定的优惠,正式购买之后,已经支付过500元定金的用户会收到100元优惠券,200元定金的用户可以收到50元优惠券,没有支付过定金的只能进入普通购买方式,也就是没有优惠券。相关的字段有这么几种:
oederType:订单类型,为1代表500元定金用户,2代表200元定金用户,3为普通购买用户;
//职责链模式学习 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{ return 'nextSuccessor'; } };
接下来需要把函数包装进职责链节点:
//职责链包装 var Chain=function(fn){ this.fn=fn; this.nextSuccessor=null; }; Chain.prototype.setNextSuccessor=function(successor){ return this.nextSuccessor=successor; }; Chain.prototype.passRequest=function(){ var ret=this.fn.apply(this,arguments); if(ret=='nextSuccessor'){ //console.log(this.nextSuccessor.fn.name); return this.nextSuccessor&&this.nextSuccessor.passRequest.apply(this.nextSuccessor,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,100);//输出 500元定金,得到100元优惠券 chainOrder500.passRequest(2,true,100);//输出200元定金,得到50元优惠券 chainOrder500.passRequest(1,false,0); //输出 手机库存不足
三、异步的职责链
在之前的职责链中,我们让每个节点都返回一个特定值nextSuccessor,来表示将请求传递给下一个节点,在现实生活中,经常会遇到一些异步的问题,比如发送ajax请求,由ajax请求的结果来决定是否传递给下一个节点,此时同步返回nextSuccessor已经没有意义了,需要给Chain类增加Chain.prototype.next,用于手动传递请求给下一个节点。
Chain.prototype.next=function(){ return this.nextSuccessor&&this.nextSuccessor.passRequest.apply(this.nextSuccessor,arguments); };
四、使用AOP的方式实现职责链
之前的案例采用Chain将普通函数包装成职责链的节点,实际上有一种更加方便的方式来创建职责链。
//使用AOP的方式创建职责链 Function.prototype.after=function(afterFn){ var self=this; return function(){ var ret=self.apply(this,arguments); if(ret==='nextSuccessor'){ ret=afterFn.apply(this.arguments); } return ret; }; }; var order=order500.after(order200).after(orderNormal); order(1,false,500);