• js 设计模式——状态模式


    状态模式

    允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

    简单的解释一下:

    • 第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。
    • 第二部分是从客户的角度来看,我们使用的对象,在不同的状态下具有截然不同的行为,这个对象看起来是从不同的类中实例化而来的,实际上这是使用了委托的效果。

    现在举一个网上比较多的例子,没错就是电灯的例子(不要烦,请耐心往下看)

    // 首先定义了一个Light类
    class Light {
      // 定义一个状态变量
      constructor(){
        this.state = 'off'
      }
      // 定义一个改变状态的方法
      change(){
        if(this.state === 'off'){
          console.log('开灯')
          this.state = 'on'
        } else {
          console.log('关灯')
          this.state = 'off'
        }
      }
    }
    // 创建实例
    let light = new Light()
    // 调用方法
    light.change()
    

    当当当当,到此我们已经编写了一个状态机,逻辑简单又缜密,看起来还有那么点无懈可击。BUT,你懂的事实并非如此,人生也没那么多的如意。随着人类的进步,需求也不(de) 断(cuo) 进(jin) 步(chi)(●'◡'●),于是新的电灯出现了,这电灯可厉害了,第一次点击弱光,再次点击强光,再点七彩光,再点emmm关了。

    按我们上面的逻辑来写,那可就刺激了:

    • 首先违反了开闭原则,每次改动都要更改change()方法,使得方法变得不稳定
    • 状态切换的不明显,无法一目了然的明白一共有多少种状态
    • 状态之间的切换关系,不过是往change()方法里添加if、else语句,是change()方法更加难阅读和维护

    那怎么办呢?有首歌怎么唱来着“新的电灯已经出现,怎么能够停滞不前”,哈哈,所以状态模式来了~~~

    因为电灯的例子太无聊了,所以我换了一个例子但是呢意思是一样滴:

      // 单曲循环类
      class SingleCycle{
        constructor(self){
          this._self = self
        }
        modeSwitch(){
          console.log('现在是单曲循环')
          this._self.setState( this._self.listCirculation )
        }
      }
      // 列表循环类
      class ListCirculation{
        constructor(self){
          this._self = self
        }
        modeSwitch(){
          console.log('现在是列表循环')
          this._self.setState( this._self.sequentialPlay )
        }
      }
      // 顺序播放类
      class SequentialPlay{
        constructor(self){
          this._self = self
        }
        modeSwitch(){
          console.log('现在是顺序播放')
          this._self.setState( this._self.shufflePlay )
        }
      }
      // 随机播放类
      class ShufflePlay{
        constructor(self){
          this._self = self
        }
        modeSwitch(){
          console.log('现在是随机播放')
          this._self.setState( this._self.singleCycle )
        }
      }
      // 音乐类
      class Music{
        constructor(){
          // 为每个状态都创建一个状态对象
          this.singleCycle = new SingleCycle(this)
          this.listCirculation = new ListCirculation(this)
          this.sequentialPlay = new SequentialPlay(this)
          this.shufflePlay = new ShufflePlay(this)
          // 定义初始状态为顺序播放
          this.currState = this.sequentialPlay
        }
        // 切换播放模式
        changeMode(){
          this.currState.modeSwitch()
        }
        // 下一次点击时的播放状态
        setState(newState){
          this.currState = newState;
        }
      }
      // 实例化音乐类
      let music = new Music()
      // 调用切换播放模式方法
      music.changeMode()
    

    好了,到此我们改编完成,如果你没有懵掉,good,如果你懵掉了,往下看:

    1. 首先我们定义了4个状态类 SingleCycle(单曲循环)ListCirculation(列表循环)SequentialPlay(顺序播放)ShufflePlay(随机播放)
    2. 每个状态类都定义了一个变量 _self 来接收 Music(音乐类) 传过来的 this,还有一个方法 modeSwitch(状态更改),用来改变下一次要播放的状态
    3. 然后定义了一个 Music(音乐类) ,首先在里面为每一个状态都创建了一个状态对象,还定义了一个变量 currState 来记录下一次点击时的状态。
    4. 最后就是Music(音乐类)里面定义的两种方法 changeMode(切换播放模式)setState(下一次点击时的播放状态) 。当我们点击切换模式的时候,在 changeMode(切换播放模式) 中去调用之前定义好的状态类中的 modeSwitch(状态更改) 方法,完成模式切换的同时调用Music(音乐类)中的 setState(下一次点击时的播放状态) 方法来对状态进行改变,保证下一次点击时切换不同的模式。

    通过上面的方法可以看出:

    1. 我们可以在 Music(音乐类) 中清楚的知道一共有多少个状态,同时 Music(音乐类) 中不再进行任何实质性的操作,而是通过 this.currState.modeSwitch() 交给了当前持有的状态对象去执行
    2. 状态的切换规律被事先在每一个状态类中定义好了,在 Music(音乐类) 中没有任何一个和状态切换相关的条件分支语句

    悄悄地说,上面的方法还阔以再完美一点呦


    小小的拓展

    通过上面的介绍我们了解到了每一个状态类都有一个 modeSwitch() 方法,也就意味着我们每次添加状态类都要写一个方法,问题来了,人非圣贤,孰能无过?所以咧难免会丢掉的嘛!

    然后做一些小小的优化:

    // 定义一个State类
    class State{
      constructor(self){
        this._self = self
      }
      modeSwitch(){
       throw new Error( '父类的 modeSwitch 方法必须被重写' )
      }
    }
    
    // 状态类(举一个为例)
    
    // 单曲循环类(继承State类)
    class SingleCycle extends State{
      modeSwitch(){
        console.log('现在是单曲循环')
        this._self.setState( this._self.listCirculation )
      }
    }
    

    好了完成,当某一天我们忘了写方法,也能够快速的定位到错误


    目前对于状态模式的理解就这么多,以后有了新的理解会继续更新的,溜了溜了(~ ̄▽ ̄)~

  • 相关阅读:
    C#将datatable生成easyui的绑定tree 的json数据格式
    asp.net DataTable转JSON
    asp.net面试题
    windows笔记线程的一些性质
    windows笔记创建线程的另一个函数_beginthreadex
    windows笔记【内核对象线程同步】等待定时器内核对象
    windows笔记用哪个创建线程CreateThread还是_beginthread
    windows笔记【内核对象线程同步】信标内核对象
    windows笔记【内核对象线程同步】事件内核对象
    windows笔记【内核对象线程同步】等待函数
  • 原文地址:https://www.cnblogs.com/loveyt/p/11403784.html
Copyright © 2020-2023  润新知