状态模式核心概念
- 状态模式 (State Pattern):是一种行为型设计模式,核心思想是:将对象的不同状态封装成独立的状态类,让对象的行为随着其状态的改变而改变,而非通过大量的 if/else 或 switch 语句控制;
- 类图:
- 实现代码:
// 1. 抽象状态类(对应类图中的 State) abstract class OrderState { /** * 处理当前状态的核心行为 * @param context 上下文(订单对象),用于状态切换 */ abstract handle(context: OrderContext): void; /** * 获取状态名称(便于调试/展示) */ abstract getName(): string; } // 2. 具体状态类:待支付(对应类图中的 StateA) class PendingPaymentState extends OrderState { handle(context: OrderContext): void { console.log(`当前订单状态:${this.getName()} → 执行支付逻辑`); // 支付成功后,切换到“已支付”状态 context.setState(new PaidState()); } getName(): string { return "待支付"; } } // 3. 具体状态类:已支付(对应类图中的 StateB) class PaidState extends OrderState { handle(context: OrderContext): void { console.log(`当前订单状态:${this.getName()} → 执行发货/确认收货逻辑`); // 确认收货后,切换到“已完成”状态 context.setState(new CompletedState()); } getName(): string { return "已支付"; } } // 4. 具体状态类:已完成(对应类图中的 StateC) class CompletedState extends OrderState { handle(context: OrderContext): void { console.log(`当前订单状态:${this.getName()} → 订单已结束,无后续操作`); // 已完成状态无需切换 } getName(): string { return "已完成"; } } // 5. 上下文类:订单(对应类图中的 Context) class OrderContext { private state: OrderState; // 持有当前状态引用 constructor() { // 订单初始状态:待支付 this.state = new PendingPaymentState(); } /** * 切换状态(核心方法) * @param newState 新状态 */ setState(newState: OrderState): void { console.log(`状态切换:${this.state.getName()} → ${newState.getName()}`); this.state = newState; } /** * 对外暴露的统一操作入口(触发当前状态的行为) */ execute(): void { this.state.handle(this); } /** * 获取当前状态名称(便于外部查询) */ getCurrentStateName(): string { return this.state.getName(); } } // ==================== 测试代码 ==================== const order = new OrderContext(); // 第一次执行:待支付 → 已支付 order.execute(); // 输出: // 当前订单状态:待支付 → 执行支付逻辑 // 状态切换:待支付 → 已支付 // 第二次执行:已支付 → 已完成 order.execute(); // 输出: // 当前订单状态:已支付 → 执行发货/确认收货逻辑 // 状态切换:已支付 → 已完成 // 第三次执行:已完成(无状态切换) order.execute(); // 输出: // 当前订单状态:已完成 → 订单已结束,无后续操作 console.log("最终订单状态:", order.getCurrentStateName()); // 输出:最终订单状态:已完成
状态模式的常用场景
表单状态流转
-
场景:表单 (草稿 / 编辑中 / 已提交 / 审核中 / 审核通过 / 审核驳回);
-
示例:编辑中状态下可提交表单,提交后切换到审核中;审核驳回后切回编辑中,审核通过后切到已完成;
// 抽象状态类 abstract class FormState { abstract handle(context: FormContext): void; abstract getName(): string; } // 具体状态:草稿 class DraftState extends FormState { handle(context: FormContext) { console.log(`[表单] ${this.getName()} → 进入编辑模式`); context.setState(new EditingState()); } getName() { return "草稿"; } } // 具体状态:编辑中 class EditingState extends FormState { handle(context: FormContext) { console.log(`[表单] ${this.getName()} → 提交表单`); context.setState(new SubmittedState()); } getName() { return "编辑中"; } } // 具体状态:已提交 class SubmittedState extends FormState { handle(context: FormContext, isApproved: boolean = true) { console.log(`[表单] ${this.getName()} → 审核中...`); // 审核结果分支 context.setState(isApproved ? new ApprovedState() : new RejectedState()); } getName() { return "已提交"; } } // 具体状态:审核通过 class ApprovedState extends FormState { handle(context: FormContext) { console.log(`[表单] ${this.getName()} → 表单生效,流程结束`); } getName() { return "审核通过"; } } // 具体状态:审核驳回 class RejectedState extends FormState { handle(context: FormContext) { console.log(`[表单] ${this.getName()} → 退回编辑模式`); context.setState(new EditingState()); } getName() { return "审核驳回"; } } // 表单上下文 class FormContext { private state: FormState; constructor() { this.state = new DraftState(); // 初始状态:草稿 } setState(newState: FormState) { console.log(`状态切换:${this.state.getName()} → ${newState.getName()}`); this.state = newState; } // 通用操作入口 execute(isApproved?: boolean) { if (this.state instanceof SubmittedState) { (this.state as SubmittedState).handle(this, isApproved); } else { this.state.handle(this); } } getCurrentState() { return this.state.getName(); } } // 测试 const form = new FormContext(); form.execute(); // 草稿 → 编辑中 form.execute(); // 编辑中 → 已提交 form.execute(true); // 已提交 → 审核通过 // form.execute(false); // 可测试驳回流程 console.log("最终状态:", form.getCurrentState());
播放器状态控制
-
场景:音频 / 视频播放器 (播放 / 暂停 / 停止 / 加载中 / 出错);
-
优势:不同状态下的行为隔离 (如 “暂停” 状态下点击播放按钮,执行 “恢复播放” 逻辑;“停止” 状态下点击播放,执行 “重新播放” 逻辑);
// 抽象状态类 abstract class PlayerState { abstract handle(context: PlayerContext): void; abstract getName(): string; } // 具体状态:加载中 class LoadingState extends PlayerState { handle(context: PlayerContext) { console.log(`[播放器] ${this.getName()} → 资源加载完成`); context.setState(new PlayingState()); } getName() { return "加载中"; } } // 具体状态:播放中 class PlayingState extends PlayerState { handle(context: PlayerContext) { console.log(`[播放器] ${this.getName()} → 暂停播放`); context.setState(new PausedState()); } getName() { return "播放中"; } } // 具体状态:暂停 class PausedState extends PlayerState { handle(context: PlayerContext) { console.log(`[播放器] ${this.getName()} → 继续播放`); context.setState(new PlayingState()); } getName() { return "暂停"; } } // 具体状态:停止 class StoppedState extends PlayerState { handle(context: PlayerContext) { console.log(`[播放器] ${this.getName()} → 重新加载`); context.setState(new LoadingState()); } getName() { return "停止"; } } // 播放器上下文 class PlayerContext { private state: PlayerState; constructor() { this.state = new LoadingState(); // 初始加载 } setState(newState: PlayerState) { console.log(`状态切换:${this.state.getName()} → ${newState.getName()}`); this.state = newState; } // 统一操作:点击播放/暂停/停止按钮 toggle() { this.state.handle(this); } // 强制停止 stop() { console.log(`[播放器] 强制停止`); this.setState(new StoppedState()); } getCurrentState() { return this.state.getName(); } } // 测试 const player = new PlayerContext(); player.toggle(); // 加载中 → 播放中 player.toggle(); // 播放中 → 暂停 player.toggle(); // 暂停 → 播放中 player.stop(); // 强制停止 → 停止 player.toggle(); // 停止 → 加载中
页面 / 组件生命周期状态
-
场景:复杂组件 (如弹窗:关闭 / 打开中 / 已打开 / 关闭中;分页组件:加载中 / 加载完成 / 加载失败);
-
优势:避免在组件中用大量变量 (isLoading/isOpen/isError) 控制行为,状态切换逻辑更清晰;
// 抽象状态类 abstract class ComponentState { abstract handle(context: ComponentContext): void; abstract getName(): string; } // 具体状态:关闭 class ClosedState extends ComponentState { handle(context: ComponentContext) { console.log(`[弹窗] ${this.getName()} → 执行打开动画`); context.setState(new OpeningState()); } getName() { return "关闭"; } } // 具体状态:打开中(动画执行) class OpeningState extends ComponentState { handle(context: ComponentContext) { console.log(`[弹窗] ${this.getName()} → 动画完成,弹窗打开`); context.setState(new OpenedState()); } getName() { return "打开中"; } } // 具体状态:已打开 class OpenedState extends ComponentState { handle(context: ComponentContext) { console.log(`[弹窗] ${this.getName()} → 执行关闭动画`); context.setState(new ClosingState()); } getName() { return "已打开"; } } // 具体状态:关闭中(动画执行) class ClosingState extends ComponentState { handle(context: ComponentContext) { console.log(`[弹窗] ${this.getName()} → 动画完成,弹窗关闭`); context.setState(new ClosedState()); } getName() { return "关闭中"; } } // 组件上下文(弹窗) class ComponentContext { private state: ComponentState; constructor() { this.state = new ClosedState(); // 初始关闭 } setState(newState: ComponentState) { console.log(`状态切换:${this.state.getName()} → ${newState.getName()}`); this.state = newState; } // 触发组件状态变更(打开/关闭) trigger() { this.state.handle(this); } getCurrentState() { return this.state.getName(); } } // 测试 const modal = new ComponentContext(); modal.trigger(); // 关闭 → 打开中 modal.trigger(); // 打开中 → 已打开 modal.trigger(); // 已打开 → 关闭中 modal.trigger(); // 关闭中 → 关闭
游戏角色状态(小游戏 / 互动场景)
-
场景:游戏角色 (站立 / 行走 / 跳跃 / 攻击 / 受伤 / 死亡);
-
优势:不同状态下的行为独立 (如 “受伤” 状态下无法攻击,“死亡” 状态下所有操作无效);
// 抽象状态类 abstract class RoleState { abstract handle(context: RoleContext): void; abstract getName(): string; } // 具体状态:站立 class IdleState extends RoleState { handle(context: RoleContext) { console.log(`[角色] ${this.getName()} → 开始行走`); context.setState(new WalkingState()); } getName() { return "站立"; } } // 具体状态:行走 class WalkingState extends RoleState { handle(context: RoleContext) { console.log(`[角色] ${this.getName()} → 触发跳跃`); context.setState(new JumpingState()); } getName() { return "行走"; } } // 具体状态:跳跃 class JumpingState extends RoleState { handle(context: RoleContext) { console.log(`[角色] ${this.getName()} → 落地受伤`); context.setState(new HurtState()); } getName() { return "跳跃"; } } // 具体状态:受伤 class HurtState extends RoleState { handle(context: RoleContext) { console.log(`[角色] ${this.getName()} → 血量为0,死亡`); context.setState(new DeadState()); } getName() { return "受伤"; } } // 具体状态:死亡 class DeadState extends RoleState { handle(context: RoleContext) { console.log(`[角色] ${this.getName()} → 无法执行任何操作`); // 死亡状态无切换 } getName() { return "死亡"; } } // 角色上下文 class RoleContext { private state: RoleState; public hp: number = 100; // 血量(模拟状态判断条件) constructor() { this.state = new IdleState(); // 初始站立 } setState(newState: RoleState) { console.log(`状态切换:${this.state.getName()} → ${newState.getName()}`); this.state = newState; // 受伤时扣血 if (newState instanceof HurtState) this.hp -= 100; } // 执行角色动作(移动/跳跃等) doAction() { if (this.state instanceof DeadState) { console.log("[角色] 已死亡,动作无效"); return; } this.state.handle(this); } getCurrentState() { return this.state.getName(); } } // 测试 const role = new RoleContext(); role.doAction(); // 站立 → 行走 role.doAction(); // 行走 → 跳跃 role.doAction(); // 跳跃 → 受伤(血量归零) role.doAction(); // 受伤 → 死亡 role.doAction(); // 死亡 → 动作无效 console.log("角色血量:", role.hp); console.log("最终状态:", role.getCurrentState());
订单 / 交易状态管理(最典型)
-
以「电商订单全生命周期」为场景 (覆盖下单→支付→履约→售后→完结全流程),这个场景是前端最复杂的状态流转场景之一,包含:
- 多分支流转 (支付成功 / 失败、发货 / 取消发货、售后通过 / 驳回);
- 状态依赖 (如 “申请售后” 必须在 “已发货” 之后);
- 历史状态回溯 (取消订单后可重新下单);
- 禁止流转规则 (如 “已完结” 订单不可操作);
-
图解:
-
实现代码:
// 订单状态枚举(覆盖全生命周期) type OrderStatus = | "INIT" // 初始(未下单) | "PENDING_PAY" // 待支付 | "PAY_SUCCESS" // 支付成功 | "PAY_FAILED" // 支付失败 | "PENDING_SEND"// 待发货 | "SHIPPED" // 已发货 | "RECEIVED" // 已收货 | "REFUND_APPLY"// 申请售后 | "REFUND_PASS" // 售后通过 | "REFUND_REJECT"// 售后驳回 | "COMPLETED" // 订单完结 | "CANCELED"; // 订单取消 // 订单上下文(包含订单基础信息+状态控制) class OrderContext { public orderId: string; public amount: number; public payAmount: number = 0; public isPaid: boolean = false; public historyStatus: OrderStatus[] = []; // 历史状态回溯 private state: OrderState; // 当前状态 constructor(orderId: string, amount: number) { this.orderId = orderId; this.amount = amount; this.state = new InitState(); // 初始状态 this.recordHistory(this.state.getStatus()); } // 切换状态 setState(newState: OrderState): void { const oldStatus = this.state.getStatus(); const newStatus = newState.getStatus(); console.log(`[订单${this.orderId}] 状态变更:${oldStatus} → ${newStatus}`); this.state = newState; this.recordHistory(newStatus); } // 记录历史状态 private recordHistory(status: OrderStatus): void { if (!this.historyStatus.includes(status)) { this.historyStatus.push(status); } } // 执行状态行为(对外统一入口) execute(action: { type: string; payload?: any }): void { this.state.handle(this, action); } // 获取当前状态 getCurrentStatus(): OrderStatus { return this.state.getStatus(); } // 获取历史状态 getHistoryStatus(): OrderStatus[] { return [...this.historyStatus]; } } // 抽象状态类(定义核心行为) abstract class OrderState { // 获取当前状态标识 abstract getStatus(): OrderStatus; /** * 处理状态行为(核心方法) * @param context 订单上下文 * @param action 操作行为(类型+参数) */ abstract handle(context: OrderContext, action: { type: string; payload?: any }): void; /** * 校验状态流转是否合法(通用方法) * @param context 订单上下文 * @param allowStatus 允许流转的前置状态 * @returns 是否合法 */ protected checkAllow(context: OrderContext, allowStatus: OrderStatus[]): boolean { const current = context.getCurrentStatus(); if (!allowStatus.includes(current)) { console.error(`[订单${context.orderId}] 非法操作:当前状态${current},仅允许${allowStatus.join("/")}状态执行`); return false; } return true; } } // ==================== 具体状态实现 ==================== // 1. 初始状态(未下单) class InitState extends OrderState { getStatus(): OrderStatus { return "INIT"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "CREATE_ORDER": // 创建订单 context.setState(new PendingPayState()); break; default: console.error(`[订单${context.orderId}] 初始状态不支持${action.type}操作`); } } } // 2. 待支付状态 class PendingPayState extends OrderState { getStatus(): OrderStatus { return "PENDING_PAY"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "PAY": // 支付操作 const payAmount = action.payload?.amount || 0; if (payAmount < context.amount) { console.error(`[订单${context.orderId}] 支付金额不足:需支付${context.amount},实际支付${payAmount}`); context.setState(new PayFailedState()); return; } context.payAmount = payAmount; context.isPaid = true; context.setState(new PaySuccessState()); break; case "CANCEL_ORDER": // 取消订单 context.setState(new CanceledState()); break; default: console.error(`[订单${context.orderId}] 待支付状态不支持${action.type}操作`); } } } // 3. 支付成功状态 class PaySuccessState extends OrderState { getStatus(): OrderStatus { return "PAY_SUCCESS"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "SEND_GOODS": // 商家发货 context.setState(new PendingSendState()); break; case "CANCEL_ORDER": // 取消订单(支付成功后仅1小时内可取消) const canCancel = action.payload?.within1Hour || false; if (canCancel) { context.setState(new CanceledState()); } else { console.error(`[订单${context.orderId}] 支付成功超过1小时,不可取消`); } break; default: console.error(`[订单${context.orderId}] 支付成功状态不支持${action.type}操作`); } } } // 4. 支付失败状态 class PayFailedState extends OrderState { getStatus(): OrderStatus { return "PAY_FAILED"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "RETRY_PAY": // 重试支付 context.setState(new PendingPayState()); break; case "CANCEL_ORDER": // 取消订单 context.setState(new CanceledState()); break; default: console.error(`[订单${context.orderId}] 支付失败状态不支持${action.type}操作`); } } } // 5. 待发货状态 class PendingSendState extends OrderState { getStatus(): OrderStatus { return "PENDING_SEND"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "CONFIRM_SEND": // 确认发货 context.setState(new ShippedState()); break; case "CANCEL_SEND": // 取消发货 context.setState(new PaySuccessState()); break; default: console.error(`[订单${context.orderId}] 待发货状态不支持${action.type}操作`); } } } // 6. 已发货状态 class ShippedState extends OrderState { getStatus(): OrderStatus { return "SHIPPED"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "CONFIRM_RECEIVE": // 确认收货 context.setState(new ReceivedState()); break; case "APPLY_REFUND": // 申请售后 if (this.checkAllow(context, ["SHIPPED"])) { context.setState(new RefundApplyState()); } break; default: console.error(`[订单${context.orderId}] 已发货状态不支持${action.type}操作`); } } } // 7. 已收货状态 class ReceivedState extends OrderState { getStatus(): OrderStatus { return "RECEIVED"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "COMPLETE_ORDER": // 完成订单 context.setState(new CompletedState()); break; case "APPLY_REFUND": // 申请售后(7天内) const within7Days = action.payload?.within7Days || false; if (within7Days && this.checkAllow(context, ["RECEIVED"])) { context.setState(new RefundApplyState()); } else { console.error(`[订单${context.orderId}] 已收货超过7天,不可申请售后`); } break; default: console.error(`[订单${context.orderId}] 已收货状态不支持${action.type}操作`); } } } // 8. 申请售后状态 class RefundApplyState extends OrderState { getStatus(): OrderStatus { return "REFUND_APPLY"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "PASS_REFUND": // 售后通过 context.setState(new RefundPassState()); break; case "REJECT_REFUND": // 售后驳回 context.setState(new RefundRejectState()); break; case "CANCEL_REFUND": // 取消售后 // 回溯到上一个状态(已发货/已收货) const history = context.getHistoryStatus(); const prevStatus = history[history.length - 2] || "RECEIVED"; switch (prevStatus) { case "SHIPPED": context.setState(new ShippedState()); break; case "RECEIVED": context.setState(new ReceivedState()); break; default: context.setState(new CompletedState()); } break; default: console.error(`[订单${context.orderId}] 申请售后状态不支持${action.type}操作`); } } } // 9. 售后通过状态 class RefundPassState extends OrderState { getStatus(): OrderStatus { return "REFUND_PASS"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "COMPLETE_REFUND": // 完成退款 context.setState(new CompletedState()); break; default: console.error(`[订单${context.orderId}] 售后通过状态不支持${action.type}操作`); } } } // 10. 售后驳回状态 class RefundRejectState extends OrderState { getStatus(): OrderStatus { return "REFUND_REJECT"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "REAPPLY_REFUND": // 重新申请售后 context.setState(new RefundApplyState()); break; case "ABANDON_REFUND": // 放弃售后 context.setState(new ReceivedState()); break; default: console.error(`[订单${context.orderId}] 售后驳回状态不支持${action.type}操作`); } } } // 11. 订单完结状态(终态,不可操作) class CompletedState extends OrderState { getStatus(): OrderStatus { return "COMPLETED"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { console.error(`[订单${context.orderId}] 订单已完结(${this.getStatus()}),不支持任何操作`); } } // 12. 订单取消状态(可回溯) class CanceledState extends OrderState { getStatus(): OrderStatus { return "CANCELED"; } handle(context: OrderContext, action: { type: string; payload?: any }): void { switch (action.type) { case "RECREATE_ORDER": // 重新下单 context.setState(new PendingPayState()); break; default: console.error(`[订单${context.orderId}] 订单已取消(${this.getStatus()}),仅支持重新下单`); } } } // ==================== 测试复杂状态流转 ==================== const order = new OrderContext("ORD20260226001", 199); // 1. 初始→待支付→支付成功→待发货→已发货 order.execute({ type: "CREATE_ORDER" }); order.execute({ type: "PAY", payload: { amount: 199 } }); order.execute({ type: "SEND_GOODS" }); order.execute({ type: "CONFIRM_SEND" }); // 2. 已发货→申请售后→售后驳回→重新申请→售后通过→完成退款→完结 order.execute({ type: "APPLY_REFUND" }); order.execute({ type: "REJECT_REFUND" }); order.execute({ type: "REAPPLY_REFUND" }); order.execute({ type: "PASS_REFUND" }); order.execute({ type: "COMPLETE_REFUND" }); // 3. 尝试操作完结订单(禁止) order.execute({ type: "APPLY_REFUND" }); // 4. 打印历史状态 console.log("订单历史状态:", order.getHistoryStatus()); console.log("最终状态:", order.getCurrentStatus());
状态模式的优势
-
单一职责原则:将与特定状态相关的行为封装在独立的类中;
-
开闭原则:添加新状态无需修改现有状态类和上下文;
-
消除条件语句:消除大量的条件分支语句;
-
状态转换明确:状态间的转换更加清晰和可控;
注意事项
-
类数量增加:每个状态都需要一个类,可能导致类数量过多;
-
状态共享:如果状态对象没有实例变量,可以被多个上下文共享;
-
状态转换逻辑:可以在状态类或上下文类中定义转换逻辑;
-
性能考虑:状态切换频繁时,需要注意对象创建的开销;
迭代器模式(行为型模式)
上一篇