State Pattern
用状态模式管理对象的状态切换和行为变化
问题
什么是状态模式?如何用 JavaScript 实现?
解答
状态模式让对象在内部状态改变时改变其行为,看起来像是改变了对象的类。
基本实现
以音乐播放器为例:
// 状态接口
class State {
play() {}
pause() {}
stop() {}
}
// 具体状态:停止状态
class StoppedState extends State {
constructor(player) {
super();
this.player = player;
}
play() {
console.log('开始播放');
this.player.setState(this.player.playingState);
}
pause() {
console.log('已经停止,无法暂停');
}
stop() {
console.log('已经是停止状态');
}
}
// 具体状态:播放状态
class PlayingState extends State {
constructor(player) {
super();
this.player = player;
}
play() {
console.log('已经在播放');
}
pause() {
console.log('暂停播放');
this.player.setState(this.player.pausedState);
}
stop() {
console.log('停止播放');
this.player.setState(this.player.stoppedState);
}
}
// 具体状态:暂停状态
class PausedState extends State {
constructor(player) {
super();
this.player = player;
}
play() {
console.log('继续播放');
this.player.setState(this.player.playingState);
}
pause() {
console.log('已经暂停');
}
stop() {
console.log('停止播放');
this.player.setState(this.player.stoppedState);
}
}
// 上下文:播放器
class MusicPlayer {
constructor() {
// 初始化所有状态
this.stoppedState = new StoppedState(this);
this.playingState = new PlayingState(this);
this.pausedState = new PausedState(this);
// 初始状态
this.state = this.stoppedState;
}
setState(state) {
this.state = state;
}
// 委托给当前状态处理
play() {
this.state.play();
}
pause() {
this.state.pause();
}
stop() {
this.state.stop();
}
}
// 使用
const player = new MusicPlayer();
player.play(); // 开始播放
player.pause(); // 暂停播放
player.play(); // 继续播放
player.stop(); // 停止播放
player.pause(); // 已经停止,无法暂停
简化版:用对象字面量
// 状态机配置
const stateMachine = {
stopped: {
play: 'playing',
pause: null,
stop: null
},
playing: {
play: null,
pause: 'coulg',
stop: 'stopped'
},
paused: {
play: 'playing',
pause: null,
stop: 'stopped'
}
};
class SimplePlayer {
constructor() {
this.state = 'stopped';
}
transition(action) {
const nextState = stateMachine[this.state][action];
if (nextState) {
console.log(`${this.state} -> ${nextState}`);
this.state = nextState;
} else {
console.log(`无法在 ${this.state} 状态执行 ${action}`);
}
}
play() { this.transition('play'); }
pause() { this.transition('pause'); }
stop() { this.transition('stop'); }
}
const player = new SimplePlayer();
player.play(); // stopped -> playing
player.pause(); // playing -> paused
player.play(); // coulg -> playing
实际应用:订单状态
class Order {
constructor() {
this.states = {
pending: {
pay: () => {
console.log('支付成功');
this.state = 'paid';
},
cancel: () => {
console.log('订单取消');
this.state = 'cancelled';
}
},
paid: {
ship: () => {
console.log('已发货');
this.state = 'shipped';
},
refund: () => {
console.log('退款成功');
this.state = 'refunded';
}
},
shipped: {
receive: () => {
console.log('确认收货');
this.state = 'completed';
}
},
completed: {},
cancelled: {},
refunded: {}
};
this.state = 'pending';
}
dispatch(action) {
const handler = this.states[this.state][action];
if (handler) {
handler();
} else {
console.log(`当前状态 ${this.state} 不支持 ${action} 操作`);
}
}
}
const order = new Order();
order.dispatch('pay'); // 支付成功
order.dispatch('ship'); // 已发货
order.dispatch('receive'); // 确认收货
order.dispatch('refund'); // 当前状态 completed 不支持 refund 操作
关键点
- 将状态相关的行为封装到独立的状态类中,消除大量条件判断
- 状态切换由状态对象自己控制,符合开闭原则
- 上下文对象持有当前状态,并将请求委托给状态对象处理
- 适用于对象行为随状态改变而改变的场景(订单、流程、游戏等)
- 状态较少时可用对象字面量简化,复杂场景用类实现
目录