一. 概念
状态模式是一种行为型设计模式,通过将对象在不同状态下的行为抽象为独立的对象,形成状态-行为映射。和策略模式有点像。
关键区别点如下:
- 状态对象可以自主控制状态转换,而策略对象不会自己更换策略
- 状态模式关注对象在不同状态下的行为差异,策略模式注重算法的封装与替换
- 状态模式的状态类通常需要引用当前对象(this.light),而策略类一般不需要
- 状态模式更强调生命周期内的状态变化,策略模式更强调运行时的算法选择
二. 应用场景举例
1. 举一个打开/关闭开关的案例
1. 常规方案
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按下开关</button>
<script>
class Light {
constructor() {
this.state = 'off'; // 给电灯设置初始状态 off
this.button = null; // 电灯开关按钮
}
init () {
this.button = document.querySelector('button'),
self = this;
this.button.onclick = function () {
self.buttonWasPressed();
}
}
buttonWasPressed () {
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();
</script>
</body>
</html>
2. 利用状态模式
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const FSM = {
off: {
buttonWasPressed: function () {
console.log('关灯');
this.button.innerHTML = '下一次按我是开灯';
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function () {
console.log('开灯');
this.button.innerHTML = '下一次按我是关灯';
this.currState = FSM.off;
}
}
};
class Light {
constructor() {
this.currState = FSM.off; // 设置当前状态
this.button = null;
}
init () {
var button = document.createElement('button'),
self = this;
button.innerHTML = '已关灯';
this.button = document.body.appendChild(button);
this.button.onclick = function () {
self.currState.buttonWasPressed.call(self); // 把请求委托给 FSM 状态机
}
}
}
const light = new Light();
light.init();
</script>
</body>
</html>
两种方式对比:
方案1:
适合场景: 状态简单、需求固定,且无扩展需求的系统。
优点: 代码简洁,易于快速实现。
缺点: 状态或行为增加时代码膨胀(如需添加 dim 状态需添加 else if)。
方案2:
适合场景: 需要频繁修改或扩展的状态系统(如游戏、设备控制等)。
优点: 扩展性强,维护成本低,逻辑清晰。
缺点: 初期代码量略多,需理解状态机设计模式。
总结:
状态机(方案二) 是模式化、解耦的优雅实现,适合复杂场景。
条件判断(方案一) 是快速实现的简易方案,适合简单需求。
2. 当开关的状态有多种时举例
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按下开关</button>
<script>
class OffLightState {
constructor(light) {
this.light = light;
}
buttonWasPressed () {
alert('弱光');
this.light.setState(this.light.weakLightState);
}
}
class WeakLightState {
constructor(light) {
this.light = light;
}
buttonWasPressed () {
alert('强光');
this.light.setState(this.light.strongLightState);
}
}
class StrongLightState {
constructor(light) {
this.light = light;
}
buttonWasPressed () {
alert('关灯');
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.currState = this.offLightState;
this.button = null;
}
init () {
this.button = document.querySelector('button');
this.button.onclick = () => this.currState.buttonWasPressed();
}
setState (newState) {
this.currState = newState;
}
}
const light = new Light();
light.init();
</script>
</body>
</html>
构造Light实例时
javascript
Light()
→ 初始化3个状态实例并传入自身(this)
→ 设置初始状态为关灯状态
→ 预留按钮变量
设计优势
- 可扩展性:增加新状态仅需添加新状态类,不修改已有代码
- 维护简单:状态逻辑与主类解耦,修改某个状态不会影响其他部分
- 清晰的状态转移:每个状态明确定义迁移目标,避免条件嵌套