关键词:行为型 状态模式 StatePattern
解决什么问题?
状态机 State Machine:
一种数学模型,用于描述状态转换的逻辑。常用实现的方法:分支逻辑法、查表法、状态模式。
有三部分组成:状态(State)、事件(Event)、动作(Action)。
事件(转移条件)触发状态的转移及动作的执行。其中,动作不是必须的。
状态模式是状态机的一种实现方式。
实例对象内部状态发生改变,方法也发生改变。看上去就像换了个所属类一样。
应用场景
需要根据自身当前状态执行不同方法。
相似状态或者状态机里各个条件中有许多重复代码。将公用代码抽取到抽象基类中来减少重复。
具体实践
状态模式的关键是区分事物内部的状态,内部状态的改变影响事物的行为的改变。
简单举例:
灯亮,按开关,灯灭。
灯暗,按开关,灯亮。
同一个开关按钮,在不同的状态下,表现行为不一样。
// 非状态模式 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()
结构看上去与策略模式相似,不同点在于:
- 状态之间是知道有其他状态存在的,且能触发从一个状态到另一个状态。
- 策略则完全不知道其他策略的存在。
状态可被视为策略的扩展。两者都基于组合机制:通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。
参考资料