• js-设计模式-状态模式(1)-电灯开关


    场景

    一般写法,使用if判断状态,让状态下移

    let Light = function() {
      this.state = 'off'
      this.button = null
    }
    
    let Button = {
      name: 'button',
      innerHTML: ''
    }
    Light.prototype.init = function() {
      let button = Button
      button.innerHTML = '开关'
      this.button = button
      this.button.click = () => {
        this.buttonWasPressed()
      }
    }
    Light.prototype.buttonWasPressed = function() {
      if (this.state == 'off') {
        this.state = 'on'
      } else {
        this.state = 'off'
      }
    }
    let light = new Light()
    light.init()
    console.log('light.state', light.state)
    light.button.click()
    console.log('light.state', light.state)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    更多状态的场景

    
    Light.prototype.buttonWasPressed = function() {
      if (this.state == 'off') {
        this.state = 'weakLight'
      } else if(this.state == 'weakLight'){
        this.state = 'strongLight'
      } else if(this.state == 'strongLight'){
        this.state = 'off'
      }
    }
    
    let light = new Light()
    light.init()
    console.log('light.state', light.state)
    light.button.click()
    console.log('light.state', light.state)
    light.button.click()
    console.log('light.state', light.state)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    思想:不同即须抽离,低内聚。
    分离不同的状态数据独立处理,主体通过切换状态类来控制状态的切换,将状态的切换权移交给状态本身。

    状态模式

    let OffLightState = function(light) {
      this.light = light
      this.msg = 'off' // 存储分散的状态数据,分离状态差异。
    }
    OffLightState.prototype.buttonWasPressed = function() {
      // 预设好下一个状态
      this.light.setState(this.light.weakLightState) // 调用主体的方法来设置主体的状态。
    }
    let WeakLightState = function(light) {
      this.light = light
      this.msg = 'weak'
    }
    WeakLightState.prototype.buttonWasPressed = function() {
      this.light.setState(this.light.strongLightState)
    }
    let StrongLightState = function(light) {
      this.light = light
      this.msg = 'strong'
    }
    StrongLightState.prototype.buttonWasPressed = function() {
      this.light.setState(this.light.offLightState)
    }
    let Light = function() {
      // 本体存储所有状态。
      this.offLightState = new OffLightState(this) // 实例化并绑定好每一个状态
      this.weakLightState = new WeakLightState(this)
      this.strongLightState = new StrongLightState(this)
      this.button = null
    }
    let Button = {
      name: 'button',
      innerHTML: ''
    }
    Light.prototype.init = function() {
      let button = Button
      button.innerHTML = '开关'
      this.button = button
      this.setState(this.offLightState)
      this.button.click = () => {
        // 每次按下就改变状态,让状态自己来控制下一个状态是谁。
        // 将状态改变的按钮操作分离出去,自己只保留状态设置接口。
        // 每次当前状态按下时设置下一个状态,通过状态自身来传递,而不是主体Light。
        this.currState.buttonWasPressed() 
      }
    }
    Light.prototype.setState = function(newState) {
      // 设置当前状态实例。
      this.currState = newState 
    }
    
    let light = new Light()
    light.init()
    console.log('light.currState', light.currState.msg)
    light.button.click()
    console.log('light.currState', light.currState.msg)
    light.button.click()
    console.log('light.currState', light.currState.msg)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    新增一个状态改变三个地方,状态类+按钮方法,且增加后倒数第二个需要调整状态顺序,实例
    书中针对新增类需要新增方法的遗漏的补充做法是新增一个父类,并要求子类必须重写父类的方法,否则抛错提示。

    状态模式 & yield

    let OffLightState = function() {
      this.msg = 'off'
    }
    let WeakLightState = function() {
      this.msg = 'weak'
    }
    let StrongLightState = function() {
      this.msg = 'strong'
    }
    let Light = function() {
      // 本体存储所有状态。
      this.offLightState = new OffLightState() // 实例化并绑定好每一个状态
      this.weakLightState = new WeakLightState()
      this.strongLightState = new StrongLightState()
      this.button = null
    }
    let Button = {
      name: 'button',
      innerHTML: ''
    }
    // 使用yield来控制状态顺序
    function *gen() {
      while(true) {
        yield this.setState(this.weakLightState)
        yield this.setState(this.strongLightState)
        yield this.setState(this.offLightState)
      }
    }
    Light.prototype.init = function() {
      let button = Button
      button.innerHTML = '开关'
      this.button = button
      this.setState(this.offLightState)
      let it = gen.call(this)
      this.button.click = () => {
        it.next()
      }
    }
    Light.prototype.setState = function(newState) {
      // 设置当前状态实例。
      this.currState = newState
    }
    
    let light = new Light()
    light.init()
    console.log('light.currState', light.currState)
    light.button.click()
    console.log('light.currState', light.currState)
    light.button.click()
    console.log('light.currState', light.currState)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    减少了新增类需要新增方法的操作及状态顺序的修改
    新增一个状态只需改变三个地方,状态类,实例,yield

    非继承写法

    let Light = function() {
      this.currState = FSM.off
      this.button = null
    }
    let Button = {
      name: 'button',
      msg: ''
    }
    Light.prototype.init = function() {
      let button = Button
      this.button = button
      this.button.click = () => {
        this.currState.buttonWasPressed.call(this) // 行为是外部状态的行为
      }
    }
    // 单独维护一个状态对象及状态对应的行为
    let FSM = {
      off: {
        buttonWasPressed: function() {
          this.button.msg = '关灯'
          this.currState = FSM.on // 状态是第三方的状态,不是主体的。
        }
      },
      on: {
        buttonWasPressed: function() {
          this.button.msg = '开灯'
          this.currState = FSM.off
        }
      }
    }
    
    let light = new Light()
    light.init()
    console.log('light.button', light.button.msg)
    light.button.click()
    console.log('light.button', light.button.msg)
    light.button.click()
    console.log('light.button', light.button.msg)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    上面的例子语义上的缺陷,下面使用委托来处理。
    让主体本身也有了状态和行为,但把自己的状态对应的行为委托给了外部的状态对象去处理。

    状态模式 & 委托

    let delegate = function(client, delegation) {
      return {
        buttonWasPressed: function() {
          return delegation.buttonWasPressed.apply(client, arguments)
        }
      }
    }
    let Light = function() {
      this.offState = delegate(this, FSM.off)
      this.onState = delegate(this, FSM.on)
      this.currState = this.offState
      this.button = null
    }
    let Button = {
      name: 'button',
      msg: ''
    }
    Light.prototype.init = function() {
      let button = Button
      this.button = button
      this.button.click = () => {
        this.currState.buttonWasPressed() // 按钮的语义更清晰
      }
    }
    let FSM = {
      off: {
        buttonWasPressed: function() {
          this.button.msg = '关灯'
          this.currState = this.onState
        }
      },
      on: {
        buttonWasPressed: function() {
          this.button.msg = '开灯'
          this.currState = this.offState
        }
      }
    }
    
    let light = new Light()
    light.init()
    console.log('light.button', light.button.msg)
    light.button.click()
    console.log('light.button', light.button.msg)
    light.button.click()
    console.log('light.button', light.button.msg)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    状态模式 & yield & 对象写法

    let delegate = function(client, delegation) {
      return {
        buttonWasPressed: function() {
          return delegation.buttonWasPressed.apply(client, arguments)
        }
      }
    }
    
    let FSM = { // 维护状态对应的行为
      off: {
        buttonWasPressed: function() {
          this.button.msg = '关灯'
        }
      },
      on: {
        buttonWasPressed: function() {
          this.button.msg = '开灯'
        }
      }
    }
    let Button = {
      name: 'button',
      msg: ''
    }
    let Light = {
      data() {
        return {
          // 维护内部状态变量
          offState: delegate(this, FSM.off),
          onState: delegate(this, FSM.on),
          currState: '',
          button: null,
        }
      },
      init () {
        Object.assign(this, this.data())
        this.currState = this.offState
        let button = Button
        this.button = button
        let it = this.genState()
        this.button.click = () => {
          it.next()
          this.currState.buttonWasPressed() // 按钮的语义更清晰
        }
      },
      *genState() { // 维护内部状态顺序
        while(true) {
          yield this.currState = this.offState
          yield this.currState = this.onState
        }
      }
    }
    Light.init()
    console.log('Light.button', Light.button.msg)
    Light.button.click()
    console.log('Light.button', Light.button.msg)
    Light.button.click()
    console.log('Light.button', Light.button.msg)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    最后的写法新增了yield来改进以往的状态模式,如有新的模式,请与我交流。

  • 相关阅读:
    Mybatis中拦截器的使用场景和技巧
    微信小程序:两层循环的练习,两层循环显示循环图片大图(大图显示、多层循环)
    开源生态企业反哺GitLink确实开源创新服务--DevOps引擎合作
    写Python爬虫又被屏蔽了,你现在需要一个稳定的代理IP
    MySQL高级学习八
    学习SeqGAN原理
    Arduino驱动AS7341可见光谱传感器(颜色传感器篇)
    Java Matcher.find()方法具有什么功能呢?
    Makefile 总述
    【业务功能篇91】微服务-springcloud-多线程-线程池执行顺序
  • 原文地址:https://blog.csdn.net/junjiahuang/article/details/126584380