状态模式
首发于:2022-04-02
基本概念
状态模式 (State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类,类的行为随着它的状态改变而改变。
当程序需要根据不同的外部情况来做出不同操作时,最直接的方法就是使用 switch-case
或 if-else
语句将这些可能发生的情况全部兼顾到,但是这种做法应付复杂一点的状态判断时就有点力不从心,开发者得找到合适的位置添加或修改代码,这个过程很容易出错,这时引入状态模式可以某种程度上缓解这个问题。
现实生活中的例子
交通灯,红黄绿三种状态,每种状态下车辆、行人的行为都不一样。
工作流,你提交了一个报销流程,这个流程可能处于,直属领导审批、部门领导审批、人力资源审批、完成等各种状态,每种状态的动作都不一样。
电灯的开关状态,如果是多功能的电灯可能还会有更多的状态。
女生作为你的朋友、好朋友、女朋友、老婆等不同状态,行为也不同。
在这些场景中,有以下特点:
- 对象有有限多个状态,且状态间可以相互切换;
- 各个状态和对象的行为逻辑有比较强的对应关系,即在不同状态时,对应的处理逻辑不一样。
应用场景
- 操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态,那么可以使用状态模式来将分支的处理分散到单独的状态类中;
- 对象的行为随着状态的改变而改变,那么可以考虑状态模式,来把状态和行为分离,虽然分离了,但是状态和行为是对应的,再通过改变状态调用状态对应的行为;
优缺点
优点:
- 结构相比之下清晰,避免了过多的
switch-case
或if-else
语句的使用,避免了程序的复杂性提高系统的可维护性; - 符合开闭原则,每个状态都是一个子类,增加状态只需增加新的状态类即可,修改状态也只需修改对应状态类就可以了;
- 封装性良好,状态的切换在类的内部实现,外部的调用无需知道类内部如何实现状态和行为的变换。
缺点:
- 引入了多余的类,每个状态都有对应的类,导致系统中类的个数增加。
实现
先来看一个 if-else
的例子,学了状态模式就不推荐这么做了🤪:
js
class TrafficLight {
constructor() {
this.state = '绿灯';
}
/* 设置交通灯状态 */
setState(target) {
if (target === '红灯') {
this.state = '红灯'
console.log('交通灯颜色变为 红色,行人通行 & 车辆等待')
} else if (target === '黄灯') {
this.state = '黄灯'
console.log('交通灯颜色变为 黄色,行人等待 & 车辆等待')
} else if (target === '绿灯') {
this.state = '绿灯'
console.log('交通灯颜色变为 绿色,行人等待 & 车辆通行')
} else {
console.error('交通灯还有这颜色?')
}
}
}
const trafficLight = new TrafficLight();
trafficLight.setState('红灯') // 输出: 交通灯颜色变为 红色,行人通行 & 车辆等待
trafficLight.setState('黄灯') // 输出: 交通灯颜色变为 黄色,行人等待 & 车辆等待
trafficLight.setState('绿灯') // 输出: 交通灯颜色变为 绿色,行人等待 & 车辆通行
trafficLight.setState('紫灯') // 输出: 交通灯还有这颜色?
状态模式的例子:
js
/* 抽象状态类 */
class AbstractState {
constructor() {
if (new.target === AbstractState) { // 如果是 ts 就不用搞这么麻烦了
throw new Error('抽象类不能直接实例化!')
}
}
/* 抽象方法 */
employ() {
throw new Error('抽象方法不能调用!')
}
}
/* 交通灯类 */
class State extends AbstractState {
constructor(name, desc) {
super()
this.color = { name, desc }
}
/* 覆盖抽象方法 */
employ(trafficLight) {
console.log('交通灯颜色变为 ' + this.color.name + ',' + this.color.desc)
trafficLight.setState(this)
}
}
/* 交通灯类 */
class TrafficLight {
constructor() {
this.state = null
}
/* 获取交通灯状态 */
getState() {
return this.state
}
/* 设置交通灯状态 */
setState(state) {
this.state = state
}
}
const trafficLight = new TrafficLight()
const redState = new State('红色', '行人等待 & 车辆等待')
const greenState = new State('绿色', '行人等待 & 车辆通行')
const yellowState = new State('黄色', '行人等待 & 车辆等待')
redState.employ(trafficLight) // 输出: 交通灯颜色变为 红色,行人通行 & 车辆等待
yellowState.employ(trafficLight) // 输出: 交通灯颜色变为 黄色,行人等待 & 车辆等待
greenState.employ(trafficLight) // 输出: 交通灯颜色变为 绿色,行人等待 & 车辆通行