职责链模式

作者: 会飞小超人 | 来源:发表于2018-12-29 14:34 被阅读0次

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

    有这样一个需求:

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

    这个需求写成代码就是这样:

    const 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优惠券
    

    这样的代码有下面的这些问题:

    • 大量重复代码
    • 所有逻辑都写在了一个大函数里面,维护起来很困难
    • 耦合性太强,加入某天需求变更,比如新增一个300元的优惠策略,那么几乎要改动整块代码

    使用职责链模式重写

    const order500 = function (orderType, pay, stock) {
      if (orderType === 1 && pay === true) {
        console.log('500元定金预购,得到100优惠券')
      } else {
        return 'nextSuccessor'
      }
    }
    
    const order200 = function (orderType, pay, stock) {
      if (orderType === 2 && pay === true) {
        console.log('200元定金预购,得到50优惠券')
      } else {
        return 'nextSuccessor'
      }
    }
    
    const orderNormal = function (orderType, pay, stock) {
      if (stock > 0) {
        console.log('普通购买,无优惠券')
      } else {
        console.log('手机库存不足')
      }
    }
    
    const Chain = function (fn) {
      this.fn = fn
      this.successor = null
    }
    
    Chain.prototype.setNextSuccessor = function (successor) {
      return this.successor = successor
    }
    
    Chain.prototype.passRequest = function () {
      let ret = this.fn.apply(this, arguments)
    
      if (ret === 'nextSuccessor') {
        return this.successor && this.successor.passRequest.apply(this.successor, arguments)
      }
    
      return ret
    }
    
    const chainOrder500 = new Chain(order500)
    const chainOrder200 = new Chain(order200)
    const 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(){
      // 300价位订单的处理逻辑
    };
    
    chainOrder300= new Chain( order300 );
    chainOrder500.setNextSuccessor( chainOrder300);
    chainOrder300.setNextSuccessor( chainOrder200);
    

    利用js的高级函数,还能更加简洁方便的创建职责链:

    /* 用AOP实现职责链模式 */
    Function.prototype.after=function(fn){
      let self=this
      return function(){
        let ret=self.apply(this,arguments)
        if(ret==='nextSuccessor'){
          return fn.apply(this,arguments)
        }
    
        return ret
      }
    }
    
    let order=order500.after(order200).after(orderNormal)
    
    order( 1, true, 500 );    // 输出:500元定金预购,得到100优惠券
    order( 2, true, 500 );    // 输出:200元定金预购,得到50优惠券
    order( 1, false, 500 );   // 输出:普通购买,无优惠券
    

    用AOP来实现职责链既简单又巧妙,但这种把函数叠在一起的方式,同时也叠加了函数的作用域,如果链条太长的话,也会对性能有较大的影响。

    我的理解:职责链模式算是迭代器模式的一个延伸,里面的思想与迭代器模式有些相似之处。迭代器是把队列每个元素作为输入,然后用户自己定义处理逻辑,而职责链是用户指定多个处理逻辑,处理同一个输入,只有满足条件的那个处理逻辑才能真正输出,而其他处理逻辑只是把输入传递给下一个处理逻辑。

    相关文章

      网友评论

        本文标题:职责链模式

        本文链接:https://www.haomeiwen.com/subject/xszllqtx.html