js常用設計模式11-狀態模式 1,一個簡單的電燈程序 2,改進的燈泡程序 3,light增加新的狀態 4,關鍵之處

狀態模式的關鍵是區分事物內部的狀態,事物內部狀態的改變往往會帶來事物的行爲改變

1,一個簡單的電燈程序

我們設想這麼一個場景:有一個電燈,燈開的時候,按開關,啪一下就關了;燈關的時候,按開關,啪一下就開了。
同一個開關,不同的狀態下執行,表現出來的行爲和結果不同。

 <script>
    /**
     * 一個簡單的開關燈狀態
     */

    var Light = function () {
      this.state = 'off'
      this.button = null
    }

    Light.prototype.init = function () {
      var button = document.createElement('button')
      self = this

      button.innerHTML = '開關'
      this.button = document.body.appendChild(button)
      this.button.onclick = function () {
        self.buttonWasPressed()
      }
    }

    Light.prototype.buttonWasPressed = function () {
      if (this.state === 'off') {
        console.log('開燈')
        this.state = 'on'
      } else {
        console.log('關燈')
        this.state = 'off'
      }
    }

    var light = new Light()
    light.init()

  </script>

現在我們想增加一點功能,把開燈改成弱光、強光,狀態的順序就變成了:關燈->弱光->強光。
現在我們來修改代碼:

 // 增加功能:如果需要改變光亮,關燈->弱光->強光,該怎麼辦呢?
    Light.prototype.buttonWasPressed = function () {
      if (this.state === 'off') {
        console.log('弱光')
        this.state = 'weakLight'
      } else if (this.state === 'weakLight') {
        console.log('強光')
        this.state = 'strongLight'
      } else if (this.state === 'strongLight') {
        console.log('關燈')
        this.state = 'off'
      }
    }

現在我們看到了,這段代碼的寫法是有問題的。
缺點:
1,buttonWasPressed違反 開放-封閉 原則,每次新增或修改light的狀態都要改動代碼,且代碼難以維護
2,代碼邏輯封裝在buttonWasPressed中,light狀態增加,buttonWasPressed的代碼也會急劇膨脹
3,狀態切換不明顯,只在state上體現出來,很容易漏掉

2,改進的燈泡程序

一般的封裝是封裝對象的行爲,而不是對象的狀態。狀態模式不一樣,它封裝的就是事物的狀態,每個狀態都有自己的類,跟這個狀態有關的行爲都被封裝在類的內部。
對於我們現在的功能來說,button被按下的時候,只需要在上下文中,把這個請求委託給當前的狀態對象即可。
同時我們可以狀態的切換規則寫在狀態類中,這樣就不用寫if else語句了:

 //  添加三個狀態類
    var OffLightState = function (light) {
      this.light = light
    }
    OffLightState.prototype.buttonWasPressed = function () {
      console.log('弱光')
      this.light.setState(this.light.weakLightState)
    }

    var WeakLightState = function (light) {
      this.light = light
    }
    WeakLightState.prototype.buttonWasPressed = function () {
      console.log('強光')
      this.light.setState(this.light.strongLightState)
    }

    var StrongLightState = function (light) {
      this.light = light
    }
    StrongLightState.prototype.buttonWasPressed = function () {
      console.log('關燈')
      this.light.setState(this.light.offLightState)
    }

改寫Light類,在Light類中給每個狀態類都創建對象,這樣的話我們的狀態就很清晰:

 var Light = function () {
      this.offLightState = new OffLightState(this)
      this.weakLightState = new WeakLightState(this)
      this.strongLightState = new StrongLightState(this)
      this.button = null
}

button按下的時候,不需要light對象自己處理了,交給狀態對象:

Light.prototype.init = function () {
  var button = document.createElement('button')
  self = this

  this.button = document.body.appendChild(button)
  this.button.innerHTML = '開關'

  //設置當前狀態
  this.curState = this.offLightState
  this.button.onclick = function () {
    self.curState.buttonWasPressed()
  }
}

提供一個修改light對象狀態的方法:

Light.prototype.setState = function (newState) {
  this.curState = newState
}

現在我們來try一try:

var light = new Light()
light.init()

3,light增加新的狀態

我們現在需要增加一種“超強光”的狀態,要怎麼做呢?
(1)新建狀態類:

 //現在我們添加一個新的狀態:超強光
    var SuperStrongLightState = function (light) {
      this.light = light
    }
    SuperStrongLightState.prototype.buttonWasPressed = function () {
      console.log('關燈')
      this.light.setState(this.light.offLightState)
    }

(2)修改Light類:

  //在Light類中添加新的狀態
    var Light = function () {
      this.offLightState = new OffLightState(this)
      this.weakLightState = new WeakLightState(this)
      this.strongLightState = new StrongLightState(this)
      this.superStrongLightState = new SuperStrongLightState(this)
      this.button = null
    }

(3)修改強光類的邏輯:

//修改強光類的邏輯
    StrongLightState.prototype.buttonWasPressed = function () {
      console.log('超強光')
      this.light.setState(this.light.superStrongLightState)
    }

4,關鍵之處

關鍵之處在於在Light中新建狀態類對象時,都把this也就是light對象傳進去了,這樣所有的狀態類中都可以修改同一個light對象了,這實際上起到了委託的效果。

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