《JavaScript设计模式与开发实践》——第十三章(职责链模式)学习记录

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

实际开发中的职责链模式

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

字段解释:

  1. orderType:订单类型,code的值为1的时候是500元定金用户,为2的时候是200元定金用户,为3的时候是普通购买用户。
  2. pay:表示用户是否已经支付定金,true或false,虽然用户已经下过500元定金的订单,但如果一直没有支付定金,现在只能降级进入普通购买模式。
  3. stock:表示当前用于普通购买的手机库存数量,已经支付过500元或者200元定金的用户不受此限制。
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){//200元定金购买模式
    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);//输出:500元定金预约,得到100优惠券

虽然以上代码也可以实现需求,但是难以阅读更难以维护。

用职责链模式重构代码

首先,我们要把500元订单,200元订单以及普通购买分成3个函数。

var order500 = function(orderType,pay,stock){
  if(orderType === 1 && pay == true){
    console.log('500元定金预购,得到100优惠券');
  }else {
    order200(orderType,pay,stock);//将请求传递给200元订单
  }
}
// 200元订单
var order200 = function(orderType,pay,stock){
  if(orderType === 2 && pay == true){
    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);//输出:500元定金预约,得到100优惠券
order500(1,false,200);//输出:普通购买,无优惠券
order500(2,true,500);//输出:200元定金预购,得到50优惠券
order500(3,false,500);//普通购买,无优惠券
order500(3,false,0);//输出:手机库存不足

可以看到,改过的代码结构清晰了很多,把一个大函数拆成了3个函数,去掉了许多嵌套的条件分支语句。
但是上面代码依然会有一个问题,请求在链条传递中的顺序非常僵硬,传递请求的代码被耦合在了业务函数之中,这是违反开放-封闭原则的。

灵活可拆分的职责链节点

为了让链中的各个节点可以灵活拆分和重组,首先需要改写一下分别表示3种购买模式的节点函数,如果某个节点不能处理请求,则返回一个特定的字符串‘nextSuccessor’来表示该请求需要继续往后传递:

var order500 = function(orderType,pay,stock){
  if(orderType === 1 && pay == true){
    console.log('500元定金预购,得到100优惠券');
  }else {
   return 'nextSuccessor';//我不知道下一个节点是谁,反正把请求往后面传递
  }
}
// 200元订单
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 {
    console.log('手机库存不足');
  }
}

接下来需要把函数包装进职责链节点,我们定义一个构造函数的Chain,在 new Chain的时候传递的参数即为需要被包装的函数,同时还有一个实例属性this.successor,表示在链中的下一个节点。

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个订单函数分别包装成职责链的节点:

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);//输出:500元定金预约,得到100优惠券
chainOrder500.passRequest(2,true,500);//输出:200元定金预购,得到50优惠券
chainOrder500.passRequest(3,false,500);//输出:普通购买,无优惠券
chainOrder500.passRequest(1,false,0);//输出:手机库存不足

通过改进,我们可以自由灵活地增加、移除和修改链中的节点顺序。

异步的职责链

在现实开发中,我们经常会遇到一些异步问题,比如我们要在节点函数中发起一个Ajax异步请求,异步请求返回的结果才能决定是否继续在职责链中passRequest。
这时候让节点同步返回“nextSuccessor”已经没有意义了,所以要给Chain类再增加一个原型方法Chain.prototype.next,表示手动传递请求给职责链中的下一个节点。

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();
  },1000)
})
var fn3 = new Chain(function(){
  console.log(3)
});
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest();

用AOP实现职责链

下面我们改写一下第三章的Function.prototype.after 函数:

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 supportFlash = function(){
  return true;
}
var getActiveXObject = function(){
  try{
    return new ActiveXObject("TXFTNActiveX.FINUpload");//IE上传控件
  }catch(e){
    return 'nextSuccessor';
  }
}

var getFlashUploadObj = function(){
  if(supportFlash()){//supportFlash函数未提供
      var str = '<object type="application/x-shockwave-flash"></object>'
      return $(str).appendTo($('body'))
    }
    return 'nextSuccessor';
}

var getFormUpladObj = function(){
  var str = '<form><input name="file" type="file"/></form>';//表单上传
  return $(str).appendTo($('body'));
}
var getUploadObj = getActiveXObject.after(getFlashUploadObj).after(getFormUpladObj);
console.log(getUploadObj())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值