職責鏈模式

職責鏈模式的定義是:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

有這樣一個需求:

假設我們負責一個售賣手機的電商網站,經過分別交納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來實現職責鏈既簡單又巧妙,但這種把函數疊在一起的方式,同時也疊加了函數的作用域,如果鏈條太長的話,也會對性能有較大的影響。

我的理解:職責鏈模式算是迭代器模式的一個延伸,裏面的思想與迭代器模式有些相似之處。迭代器是把隊列每個元素作爲輸入,然後用戶自己定義處理邏輯,而職責鏈是用戶指定多個處理邏輯,處理同一個輸入,只有滿足條件的那個處理邏輯才能真正輸出,而其他處理邏輯只是把輸入傳遞給下一個處理邏輯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章