中 进阶
状态模式#
一句话答案#
将状态相关行为封装到状态类中,状态切换时切换状态对象,消除复杂 if-else,适合订单状态/审批流程。
核心要点
状态模式允许对象在内部状态改变时改变行为,看起来好像修改了它的类。不同状态被封装为独立的类,行为委托给当前状态对象。
核心区别:
| 维度 | 状态模式 | 策略模式 |
|---|---|---|
| 切换方式 | 状态在内部自动转换(对客户透明) | 策略由客户端外部指定 |
| 关注点 | 对象行为随状态变化而变化 | 同一行为有不同的算法实现 |
| 状态间关系 | 各状态之间有转换关系(状态机) | 各策略之间独立,无关联 |
| 客户端感知 | 客户端不知道有几种状态 | 客户端主动选择策略 |
| 典型场景 | 订单状态机、TCP 连接状态、审批流程 | 不同排序算法、不同支付方式 |
状态模式示例——订单状态机:
// 状态接口
public interface OrderState {
void pay(OrderContext ctx);
void ship(OrderContext ctx);
void confirm(OrderContext ctx);
}
// 待支付状态
public class PendingPayState implements OrderState {
public void pay(OrderContext ctx) {
System.out.println("支付成功");
ctx.setState(new PaidState()); // 内部自动转换到已支付状态
}
public void ship(OrderContext ctx) {
System.out.println("请先支付"); // 当前状态不允许发货
}
public void confirm(OrderContext ctx) {
System.out.println("请先支付");
}
}
// 已支付状态
public class PaidState implements OrderState {
public void pay(OrderContext ctx) {
System.out.println("已支付,请勿重复支付");
}
public void ship(OrderContext ctx) {
System.out.println("发货成功");
ctx.setState(new ShippedState()); // 转换到已发货状态
}
public void confirm(OrderContext ctx) {
System.out.println("请等待发货");
}
}java一句话区分: 状态模式中,状态的切换是内部驱动的(对客户透明);策略模式中,策略的选择是外部指定的(由客户端决定)。
面试回答(2分钟版)
状态模式的核心是把不同状态下的行为封装到各自的状态类中,当对象的状态发生变化时切换到对应的状态对象,从而消除大量的 if-else 或 switch-case。比如订单系统中,订单有待支付、已支付、已发货、已完成等状态,每个状态下支付、发货、确认收货这些操作的行为完全不同。用状态模式的话,每个状态是一个独立的类,实现统一的状态接口,状态转换逻辑封装在各状态类内部。和策略模式最关键的区别在于:状态模式的切换是内部自动驱动的,客户端不需要知道有哪些状态,比如调用 pay() 后状态自动从待支付变为已支付;而策略模式是由客户端主动选择用哪个策略。状态之间有明确的转换关系形成状态机,策略之间则是独立平行的。实际项目中订单状态机、审批流程、TCP 连接状态管理都适合用状态模式。Spring 也提供了 Spring State Machine 框架来支持更复杂的状态机场景。
追问与易错
追问方向:
- “和策略模式区别?”→ 状态模式的切换由对象内部自动驱动(客户端不感知状态种类),状态之间有转换关系形成状态机;策略模式由客户端主动选择,各策略独立无关联
- “订单状态机怎么用状态模式?”→ 每个订单状态(待支付/已支付/已发货/已完成)封装为独立的状态类,实现统一接口(pay/ship/confirm),状态转换逻辑在各状态类内部完成,OrderContext 持有当前状态对象并委托调用
- “Spring State Machine 了解吗?”→ Spring 提供的状态机框架,通过配置定义状态、事件和转换规则,支持守卫条件、动作回调、持久化等,适合复杂状态机场景(如多级审批流程),比手写状态模式更规范
易错点:
- ❌ 状态模式就是 if-else 替代——核心是状态转换封装
- ❌ 混淆状态和策略——状态随时间变化策略由客户端选