状态机模式
状态机模式(State Pattern)是一种行为设计模式,它允许对象在内部状态改变时改变它的行为。简单来说,就像一个开关,不同的状态下会有不同的行为表现。
状态机由以下几个核心组件构成:
- 状态(State):对象在某一时刻的特定条件或模式。
- 事件(Event):导致状态从一个状态转换到另一个状态的动作或触发器。
- 转换(Transition):从一个状态到另一个状态的改变,通常由事件触发,并且可能伴随某些动作。
可以想象一下你的手机:
- 待机状态:屏幕黑屏,按电源键可以唤醒
- 解锁状态:屏幕亮起,可以滑动解锁
- 使用状态:可以打开应用、拨打电话等
- 充电状态:显示充电图标,限制某些功能
每个状态都有特定的行为,状态之间可以相互转换,这就是状态机的核心思想。
为什么要使用状态机模式?
传统方式的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class TrafficLight { constructor() { this.state = 'red'; } changeLight() { if (this.state === 'red') { this.state = 'green'; console.log('绿灯亮起,可以通行'); } else if (this.state === 'green') { this.state = 'yellow'; console.log('黄灯亮起,准备停车'); } else if (this.state === 'yellow') { this.state = 'red'; console.log('红灯亮起,禁止通行'); } } handleEmergency() { if (this.state === 'red') { } else if (this.state === 'green') { } else if (this.state === 'yellow') { } } }
|
状态机模式的优势
- 代码更清晰:每个状态的逻辑独立,易于理解
- 易于扩展:添加新状态不影响现有代码
- 减少bug:状态转换规则明确,避免非法状态
- 便于维护:修改某个状态的行为只需修改对应的状态类
状态机模式的实现
基础实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class State { handle(context) { throw new Error('子类必须实现handle方法'); } }
class RedLight extends State { handle(context) { console.log('红灯亮起,禁止通行'); context.setState(new GreenLight()); } }
class GreenLight extends State { handle(context) { console.log('绿灯亮起,可以通行'); context.setState(new YellowLight()); } }
class YellowLight extends State { handle(context) { console.log('黄灯亮起,准备停车'); context.setState(new RedLight()); } }
class TrafficLight { constructor() { this.state = new RedLight(); } setState(state) { this.state = state; } changeLight() { this.state.handle(this); } }
const trafficLight = new TrafficLight(); trafficLight.changeLight(); trafficLight.changeLight(); trafficLight.changeLight(); trafficLight.changeLight();
|
实际案例:音乐播放器
让我们用一个更贴近生活的例子——音乐播放器来演示状态机模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| class PlayerState { play(player) { throw new Error('子类必须实现play方法'); } pause(player) { throw new Error('子类必须实现pause方法'); } stop(player) { throw new Error('子类必须实现stop方法'); } }
class StoppedState extends PlayerState { play(player) { console.log('开始播放音乐 🎵'); player.setState(new PlayingState()); } pause(player) { console.log('当前已停止,无法暂停'); } stop(player) { console.log('当前已经是停止状态'); } }
class PlayingState extends PlayerState { play(player) { console.log('当前正在播放中'); } pause(player) { console.log('暂停播放 ⏸️'); player.setState(new PausedState()); } stop(player) { console.log('停止播放 ⏹️'); player.setState(new StoppedState()); } }
class PausedState extends PlayerState { play(player) { console.log('继续播放 ▶️'); player.setState(new PlayingState()); } pause(player) { console.log('当前已经是暂停状态'); } stop(player) { console.log('停止播放 ⏹️'); player.setState(new StoppedState()); } }
class MusicPlayer { constructor() { this.state = new StoppedState(); this.currentSong = ''; } setState(state) { this.state = state; } play() { this.state.play(this); } pause() { this.state.pause(this); } stop() { this.state.stop(this); } loadSong(song) { this.currentSong = song; console.log(`加载歌曲: ${song}`); } }
const player = new MusicPlayer(); player.loadSong('陈奕迅 - 爱情转移');
player.play(); player.play(); player.pause(); player.pause(); player.play(); player.stop(); player.pause();
|
进阶案例:订单状态管理
在电商系统中,订单状态管理是状态机模式的经典应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| class OrderState { constructor(name) { this.name = name; } pay(order) { console.log(`订单${this.name}状态下无法支付`); } ship(order) { console.log(`订单${this.name}状态下无法发货`); } receive(order) { console.log(`订单${this.name}状态下无法确认收货`); } cancel(order) { console.log(`订单${this.name}状态下无法取消`); } }
class PendingPaymentState extends OrderState { constructor() { super('待支付'); } pay(order) { console.log('✅ 支付成功,订单进入待发货状态'); order.setState(new PendingShipmentState()); } cancel(order) { console.log('❌ 订单已取消'); order.setState(new CancelledState()); } }
class PendingShipmentState extends OrderState { constructor() { super('待发货'); } ship(order) { console.log('🚚 订单已发货,进入待收货状态'); order.setState(new ShippedState()); } cancel(order) { console.log('❌ 订单已取消,将退款处理'); order.setState(new CancelledState()); } }
class ShippedState extends OrderState { constructor() { super('已发货'); } receive(order) { console.log('📦 确认收货,订单完成'); order.setState(new CompletedState()); } }
class CompletedState extends OrderState { constructor() { super('已完成'); } }
class CancelledState extends OrderState { constructor() { super('已取消'); } }
class Order { constructor(id, items) { this.id = id; this.items = items; this.state = new PendingPaymentState(); this.createTime = new Date(); } setState(state) { console.log(`订单状态变更: ${this.state.name} -> ${state.name}`); this.state = state; } pay() { this.state.pay(this); } ship() { this.state.ship(this); } receive() { this.state.receive(this); } cancel() { this.state.cancel(this); } getStatus() { return this.state.name; } }
const order = new Order('ORD001', ['iPhone 15', 'AirPods']); console.log(`订单创建,当前状态: ${order.getStatus()}`);
order.pay(); order.ship(); order.receive();
order.pay(); order.cancel();
|
使用状态机模式的最佳实践
1. 状态转换图
在实现状态机之前,先画出状态转换图:
1 2 3 4
| --[]--> --[]--> --[]--> +--[]----------+--[]-------->
|
2. 状态验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class OrderStateMachine { constructor() { this.transitions = { 'pending_payment': ['paid', 'cancelled'], 'paid': ['shipped', 'cancelled'], 'shipped': ['delivered'], 'delivered': [], 'cancelled': [] }; } canTransition(fromState, toState) { return this.transitions[fromState]?.includes(toState) || false; } transition(order, toState) { if (this.canTransition(order.currentState, toState)) { order.currentState = toState; console.log(`状态转换成功: ${toState}`); } else { console.log(`非法状态转换: ${order.currentState} -> ${toState}`); } } }
|
3. 状态持久化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class PersistentOrder extends Order { constructor(id, items) { super(id, items); } setState(state) { super.setState(state); this.saveToDatabase(); } saveToDatabase() { console.log(`保存订单 ${this.id} 状态: ${this.state.name}`); } static loadFromDatabase(id) { const orderData = { id, state: 'paid', items: ['商品1'] }; const order = new PersistentOrder(orderData.id, orderData.items); switch(orderData.state) { case 'pending_payment': order.state = new PendingPaymentState(); break; case 'paid': order.state = new PendingShipmentState(); break; } return order; } }
|
总结
状态机模式是一个非常实用的设计模式,特别适合以下场景:
- 对象行为依赖于状态:如播放器、订单、游戏角色等
- 状态转换规则复杂:有多个状态和转换条件
- 需要避免大量if-else:让代码更清晰易维护
优点
- 📝 代码清晰:每个状态的逻辑独立
- 🔧 易于扩展:添加新状态不影响现有代码
- 🐛 减少bug:状态转换规则明确
- 🔄 符合开闭原则:对扩展开放,对修改关闭
缺点
- 📈 增加类的数量:每个状态都需要一个类
- 🏗️ 结构复杂:对于简单状态可能过度设计