js常用設計模式8-職責鏈模式 1,現實中的職責鏈模式 2,實際開發中的職責鏈模式 3,用職責鏈模式重構代碼 4,職責鏈優化 5,異步的職責鏈

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

1,現實中的職責鏈模式

公交車上人很多的時候,把卡給前面的人,一個一個往前遞過去,最後刷卡

2,實際開發中的職責鏈模式

現在我們負責一個手機商城,客戶可以選擇交500元定金或者200元定金,也可以不交,現在到了正式購買階段。
優惠:

  • 500定金,得到100元優惠券
  • 200定金,得到50元優惠券
  • 無定金,普通購買模式,無優惠券
    字段:
  • orderType:訂單類型(定金購買或者普通用戶購買),1:500元定金,2:200元定金,3:無定金
  • pay:用戶是否已經支付定金,值爲true或false。如果用戶下過500元定金的訂單,但是沒有付款,則會降級爲普通購買模式
  • stock:普通購買的手機庫存,200元和500元的定金購買不在此列

代碼:

var order = function (orderType, pay, stock) {
  if (orderType === 1) {
    if (pay === true) {
      console.log('500元定金預購,得到100元優惠券')
    } else {
      if (stock > 0) {
        console.log('庫存足夠,普通購買')
      } else {
        console.log('庫存不足,無法購買')
      }
    }
  } else if (orderType === 2) {
    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, 300)

3,用職責鏈模式重構代碼

現在我們用職責鏈模式重構這段代碼。
將之前的函數拆分成分別處理500元定金、200元定金、無定金的三個函數。
先把orderType、pay、stock這三個參數傳給函數500,如果不符合條件,則傳遞向函數200,最後不行就傳給函數0

/**
 * 稍作修改,第一級別職責鏈模式
 * 問題:請求傳遞的代碼和業務邏輯代碼耦合嚴重,違反開放-封閉原則
 */

var order500 = function (orderType, pay, stock) {
  if (orderType === 1 && pay === true) {
    console.log('500元定金預購,得到100元優惠券')
  } else {
    order200(orderType, pay, stock)
  }
}

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, 300)
order500(2, true, 300)
order500(1, true, 0)
order500(3, false, 0)

但是現在的代碼耦合很嚴重,因爲裏面是嚴重的依賴關係,比如:

var order500 = function (orderType, pay, stock) {
  if (orderType === 1 && pay === true) {
    console.log('500元定金預購,得到100元優惠券')
  } else {
    order200(orderType, pay, stock)
  }
}

order500處理不了的要交給order200,但是如果有一天中間需要添加一個order300呢?想一想還是個很麻煩的事,有什麼辦法可以很好地解決這個問題呢?

4,職責鏈優化

我們現在需要的是靈活可拆分的職責鏈節點。
(1)首先,要改寫不符合要求時的情況,不要指定某一個固定的函數,而是返回一個特定的字符串“next”:

var order500 = function (orderType, pay, stock) {
  if (orderType === 1 && pay === true) {
    console.log('500元定金預購,得到100元優惠券')
  } else {
    return 'next'
  }
}

var order200 = function (orderType, pay, stock) {
  if (orderType === 2 && pay === true) {
    console.log('200元定金預購,得到50元優惠券')
  } else {
    return 'next'
  }
}

var orderNormal = function (orderType, pay, stock) {
  if (stock > 0) {
    console.log('庫存足夠,普通購買')
  } else {
    console.log('庫存不足,無法購買')
  }
}

(2)寫職責鏈節點的構造函數,將order函數包裝進去:
定義構造函數Chain,new Chain的時候傳入上面創建的order函數,同時裏面還有一個nextFn,表示在鏈中的下一個節點

// 創建一個新的類,用於提供給用戶實現自定義的業務邏輯
var Chain = function (fn) {
  this.fn = fn
  this.nextFn = null
}
Chain.prototype.setNextFn = function (nextFn) {
  //此處可以實現鏈式調用
  return this.nextFn = nextFn
}
Chain.prototype.request = function () {
  var result = this.fn.apply(this, arguments)
  //不可以用這種寫法
  // var result = this.fn(arguments)
  if (result === 'next') {
    return this.nextFn && this.nextFn.request.apply(this.nextFn, arguments)
  }
  return result
}

(3)創建節點,設置依賴順序,最後執行demo

var chainOrder500 = new Chain(order500)
var chainOrder200 = new Chain(order200)
var chainOrderNormal = new Chain(orderNormal)

chainOrder500.setNextFn(chainOrder200).setNextFn(chainOrderNormal)

chainOrder500.request(2, true, 300)

5,異步的職責鏈

有時候會有ajax這種異步請求,這時候如果要等到結果返回才能繼續執行程序,就沒啥用了
這時候同步返回的’next‘就沒用了,對於result,總是undefined
需要新的函數next,手動傳遞請求給職責鏈中的下一個節點:

Chain.prototype.next = function () {
  return this.nextFn && this.nextFn.request.apply(this.nextFn, arguments)
}

試試效果:

var f1 = new Chain(function () {
  console.log('f1')
  return 'next'
})
var f2 = new Chain(function () {
  console.log('f2')
  var self = this
  setTimeout(function () {
    self.next()
  }, 1000);
})
var f3 = new Chain(function () {
  console.log('f3')
})

f1.setNextFn(f2).setNextFn(f3)
f1.request()

6,職責鏈的優缺點
優點:
(1)解耦,只需要第一個函數處理問題就行,不要再管以前那種一堆的分支語句了
(2)可以靈活拆分,增加或者刪除節點都很簡單
(3)可以手動定位起點,不一定非要從order500開始,order300也不錯啊。但是分支語句就不行,我們不能越過某個if判斷
缺點:
(1)可能最後沒有函數能受理接收到的情況,因此需要一個保底函數
(2)代碼變複雜了,多了節點構造函數和節點對象,在一次運行中可能不起作用,性能會差一點

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