18. 状态模式(State)
18. 状态模式(State)
问题:对象有大量状态判断逻辑(长 switch),状态间转换复杂。
核心:把每个状态封装成独立类,状态切换就是切换当前状态对象。
interface State {
publish(): void;
reject(): void;
}
class Draft implements State {
constructor(private doc: ArticleDocument) {}
publish(): void {
console.log('提交审核');
this.doc.setState(new Moderation(this.doc));
}
reject(): void { console.log('草稿不能被拒绝'); }
}
class Moderation implements State {
constructor(private doc: ArticleDocument) {}
publish(): void {
console.log('审核通过,已发布');
this.doc.setState(new Published(this.doc));
}
reject(): void {
console.log('审核未通过,退回草稿');
this.doc.setState(new Draft(this.doc));
}
}
class Published implements State {
constructor(private doc: ArticleDocument) {}
publish(): void { console.log('已发布,无需重复操作'); }
reject(): void {
console.log('撤回发布');
this.doc.setState(new Draft(this.doc));
}
}
class ArticleDocument {
#state: State;
constructor() { this.#state = new Draft(this); }
setState(s: State): void { this.#state = s; }
publish(): void { this.#state.publish(); }
reject(): void { this.#state.reject(); }
}
const doc = new ArticleDocument();
doc.publish(); // 提交审核
doc.publish(); // 审核通过,已发布
doc.reject(); // 撤回发布package state
import "fmt"
type Document struct {
state State
}
func (d *Document) SetState(s State) { d.state = s }
func (d *Document) Publish() { d.state.Publish() }
func (d *Document) Reject() { d.state.Reject() }
type State interface {
Publish()
Reject()
}
type Draft struct{ doc *Document }
func (s Draft) Publish() {
fmt.Println("提交审核")
s.doc.SetState(Moderation{doc: s.doc})
}
func (s Draft) Reject() { fmt.Println("草稿不能被拒绝") }
type Moderation struct{ doc *Document }
func (s Moderation) Publish() {
fmt.Println("审核通过,已发布")
s.doc.SetState(Published{doc: s.doc})
}
func (s Moderation) Reject() {
fmt.Println("审核未通过,退回草稿")
s.doc.SetState(Draft{doc: s.doc})
}
type Published struct{ doc *Document }
func (s Published) Publish() { fmt.Println("已发布,无需重复操作") }
func (s Published) Reject() {
fmt.Println("撤回发布")
s.doc.SetState(Draft{doc: s.doc})
}实战场景
- 订单生命周期:待支付、已支付、已发货、已完成、已取消,每个状态允许的操作不同。
- 工作流审批:草稿、审核中、已通过、已驳回、已撤回之间的转换规则复杂,适合封装成状态对象。
- 播放器控制:停止、播放、暂停、缓冲、错误状态下,同一个按钮点击行为不同。
- 登录会话:未登录、已登录、过期、被封禁、多因素认证中,各状态决定可访问能力。
- WebSocket 连接:connecting、open、closing、closed、reconnecting 下的发送和重连逻辑不同。
- 游戏角色行为:站立、奔跑、跳跃、攻击、受伤、死亡各自响应输入和碰撞。
- 表单提交流程:编辑中、校验中、提交中、成功、失败状态下按钮、提示和重试行为不同。
- 文档发布:草稿、待审核、已发布、归档、撤回,每个状态限定可执行动作。
- 设备状态:离线、在线、忙碌、故障、维护中,控制命令和告警规则都不同。
- 任务调度:等待、运行中、重试中、成功、失败、取消,不同状态决定是否能重试或终止。