关于状态模式我所知道的

144 阅读2分钟

关键词:行为型 状态模式 StatePattern

解决什么问题?

状态机 State Machine:

一种数学模型,用于描述状态转换的逻辑。常用实现的方法:分支逻辑法、查表法、状态模式。

有三部分组成:状态(State)、事件(Event)、动作(Action)。

事件(转移条件)触发状态的转移及动作的执行。其中,动作不是必须的。 状态机.png

状态模式是状态机的一种实现方式。

实例对象内部状态发生改变,方法也发生改变。看上去就像换了个所属类一样。

应用场景

需要根据自身当前状态执行不同方法。

相似状态或者状态机里各个条件中有许多重复代码。将公用代码抽取到抽象基类中来减少重复。

具体实践

状态模式的关键是区分事物内部的状态,内部状态的改变影响事物的行为的改变。

简单举例:

灯亮,按开关,灯灭。

灯暗,按开关,灯亮。

同一个开关按钮,在不同的状态下,表现行为不一样。

// 非状态模式
class Light {
  constructor() {
    this.state = 'off';// 开关状态
    this.button = null;
  }
  init() {
    const btn = document.createElement('button');
    const _this = this;
    btn.innerText = '开关'; 
    this.button = document.body.appendChild(btn);
    this.button.onclick = () => {
      _this.btnPressed()
    }
  }
  btnPressed() {
    if (this.state === 'off') {
      console.log('开灯');
      this.state = 'on'
    } else if (this.state === 'on') {
      console.log('关灯');
      this.state = 'off'
    }
  }
}

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

如果增加灯光类型,就需要在 btnPressed 方法中不断添加判断。这回造成 btnPressed 需要做的事情太多,也不符合程序设计的开闭原则(需要直接改动 light 类里面的代码)。

通过状态模式改进

使每种状态和它对应的行为之间的关系局部化,这些行为被分散和封装在各自对应的状态类之中,便于阅读和管理代码。

状态之间的切换都被分布在状态类内部,这使得我们无需编写过多的条件分支语言来控制状态之间的转换。

增加新状态也只需要增加一个新的状态类,然后改变状态类之间的切换规则就可以了。

// 将状态类全部抽离
class OffLightState {
  constructor(light) {
    this.light = light
  }
  btnPressed() {
    console.log('弱光')
    this.light.setState(this.light.weakLightState)
  }
}

class WeakLightState {
  constructor(light) {
    this.light = light
  }
  btnPressed() {
    console.log('强光')
    this.light.setState(this.light.strongLightState)
  }
}

class StrongLightState {
  constructor(light) {
    this.light = light
  }
  btnPressed() {
    console.log('关灯')
    this.light.setState(this.light.offLightState)
  }
}

// 上下文类 负责切换
class Light {
  constructor() {
    this.offLightState = new OffLightState(this)
    this.weakLightState = new WeakLightState(this)
    this.strongLightState = new StrongLightState(this)
    this.button = null
  }
  init() {
    const btn = document.createElement('button');
    const _this = this;
    btn.innerText = '开关';
    this.button = document.body.appendChild(btn);
    this.curState = this.offLightState;
    this.button.onclick = () => {
      _this.curState.btnPressed()
    }
  }
  setState(newState) {
    this.curState = newState
  }
  btnPressed(){
    throw new Error('btnPressed 必须被重写')
  }
}

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

结构看上去与策略模式相似,不同点在于:

  • 状态之间是知道有其他状态存在的,且能触发从一个状态到另一个状态。
  • 策略则完全不知道其他策略的存在。

状态可被视为策略的扩展。两者都基于组合机制:通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。

参考资料

JS设计模式 状态模式

设计模式之美 状态模式

refactoringguru state