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对象了,这实际上起到了委托的效果。

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