设计模式之职责链模式

什么是职责链模式

职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象称为链中的节点。

实际开发中的例子

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

  • orderType:表示订单类型(定金用户或者普通购买用户),code 的值为1 的时候是500 元定金用户,为2 的时候是200 元定金用户,为3 的时候是普通购买用户。
  • pay:表示用户是否已经支付定金,值为true 或者false, 虽然用户已经下过500 元定金的订单,但如果他一直没有支付定金,现在只能降级进入普通购买模式。
  • 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('手机库存不足')
    }
  }
}

用职责链模式去思考,流程如下

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

其实吧,写成上面那样的代码逻辑已经很不错了,但是呢,仔细想一想,我们好像业务代码和判断逻辑揉一块了,但是如果又加了个300定金用户,我们的代码就显得扩展性没那么大了。下面我们继续修改

灵活可拆分的职责链节点

本节我们采用一种更灵活的方式,来改进上面的职责链模式,目标是让链中的各个节点可以灵活拆分和重组。首先需要改写一下分别表示3 种购买模式的节点函数,我们约定,如果某个节点不能处理请求,则返回一个特定的字符串 'nextSuccessor'来表示该请求需要继续往后面传递:

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{
    console.log( '手机库存不足' );
  }
};

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

// 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 个订单函数分别包装成职责链的节点:
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, true, 500 ); // 输出:普通购买,无优惠券
chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
// 假如某天网站运营人员又想出了支持300 元定金购买
var order300 = function(){
// 具体实现略
};
chainOrder300= new Chain( order300 );
chainOrder500.setNextSuccessor( chainOrder300);
chainOrder300.setNextSuccessor( chainOrder200);
posted @ 2021-12-09 10:33  自在一方  阅读(60)  评论(0编辑  收藏  举报