关键词:行为型 状态模式 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()
结构看上去与策略模式相似,不同点在于:
- 状态之间是知道有其他状态存在的,且能触发从一个状态到另一个状态。
- 策略则完全不知道其他策略的存在。
状态可被视为策略的扩展。两者都基于组合机制:通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。
参考资料