状态模式通过将“根据状态做不同行为”的逻辑封装为独立状态类,使分支更清晰、可扩展、易测试;核心是识别有明确含义和流转规则的语义状态(如订单的待支付、已发货等),定义统一状态接口与具体实现类,由状态类自身触发切换,上下文仅负责持有和委托。

用状态模式替代长串 if-else,核心是把“根据状态做不同行为”的逻辑,从条件判断中抽出来,封装成独立的状态类。不是去掉分支,而是让分支更清晰、可扩展、易测试。
识别适合抽取的状态边界
先别急着写代码,看清楚哪些 if-else 实际上是在响应“同一个对象的不同生命周期阶段”或“同一行为在不同上下文下的不同表现”。比如订单有「待支付」「已发货」「已完成」「已取消」;审批流程有「草稿」「待初审」「待复审」「已通过」「已驳回」。这些不是随意的布尔组合,而是有明确含义、有流转规则、可能重复出现的语义状态。
如果 if-else 的条件是像 if (user.isVip() && order.isOver1000() && !couponUsed) 这类多维度布尔叠加,且组合爆炸(8 种以上),那更适合策略模式+组合;但如果这些组合背后能归为 4~6 个稳定、可命名的状态,就适合状态模式。
定义状态接口与具体状态类
定义一个统一接口(或抽象类),声明所有状态共有的行为方法:
立即学习“Java免费学习笔记(深入)”;
interface OrderState {
void handlePayment(OrderContext context);
void handleShipping(OrderContext context);
void handleCancellation(OrderContext context);
}
每个具体状态只实现自己该做的事,不关心其他状态怎么处理:
- 待支付状态:允许支付,拒绝发货和取消(或仅允许用户取消)
- 已支付状态:允许发货,拒绝重复支付,允许客服取消
- 已发货状态:禁止支付和再次发货,允许确认收货或申请售后
每个类内不写 if 判断“当前是不是这个状态”,它本身就是这个状态——这是关键认知转变。
让上下文持有并切换状态
上下文类(如 OrderContext)持有一个 OrderState 引用,并提供状态变更入口:
class OrderContext {
private OrderState state = new PendingPaymentState();
void pay() { state.handlePayment(this); }
void ship() { state.handleShipping(this); }
void cancel() { state.handleCancellation(this); }
// 状态类调用此方法完成切换
void changeState(OrderState newState) {
this.state = newState;
}
}
注意:状态切换由状态类自己触发,不是上下文凭条件判断。例如 PaidState.handlePayment() 里不会做任何事,而 PendingPaymentState.handlePayment() 会调用 context.changeState(new PaidState())。
避免常见陷阱
-
不要在状态类里访问上下文的私有字段:通过上下文暴露必要方法(如
context.markAsPaid()),保持封装性 -
状态流转要有约束:不是所有状态都能任意跳转,可在
changeState()前加校验,或在状态类中抛出IllegalStateException - 初始状态要明确:在上下文构造时就设定好,而不是靠 if-else 初始化
- 状态类尽量无状态:不保存业务数据,只封装行为;数据存在上下文里
这样改完后,新增一种状态(比如「退款中」)只需新增一个状态类、修改少数几个已有状态的流转逻辑,完全不用碰原来的 if-else 链,也不用改上下文的行为方法签名。


















