1. 概述
本文将介绍 GoF 23 种设计模式中的行为型模式之一 —— State(状态)模式。
我们会先说明它的设计意图和适用场景,接着通过 UML 图解和代码示例来深入实现细节。重点在于:如何用状态类解耦原本散落在各处的条件逻辑,让代码更清晰、易维护。
适合正在重构复杂状态流转逻辑的同学参考,踩坑老手看了会心一笑,新手也能快速上手。
2. State 设计模式核心思想
State 模式的本质是:✅ 让对象在不改变自身类的前提下,改变其行为。
更直白点说:同一个对象,不同状态下,调用同一个方法,表现不同。
举个真实业务场景:一个快递包裹(Package)有三种状态:
- 已下单(Ordered)
- 已发货到网点(Delivered)
- 已签收(Received)
每种状态下,调用 next()
行为不同:
- 下单后 → 可发货
- 发货后 → 可签收
- 签收后 → 不能再前进
❌ 传统写法:if/else 泛滥
public class Package {
private String state;
public void nextState() {
if ("ordered".equals(state)) {
state = "delivered";
} else if ("delivered".equals(state)) {
state = "received";
} else {
System.out.println("已签收,无法继续");
}
}
}
问题很明显:
- 状态逻辑分散在各个方法中
- 新增状态要改一堆 if/else
- 不符合开闭原则(OCP)
- 单一职责被破坏
✅ State 模式解法
把每个状态封装成独立类,由当前状态对象决定行为。这样:
- 每个状态类只关心自己该做什么
- 新增状态只需新增类,无需修改现有代码
- 逻辑集中,可读性强
3. UML 结构图
核心角色说明:
- Context(上下文):如
Package
,持有当前状态对象,所有行为委托给状态实现 - State(状态接口):定义状态共有的行为(如
next()
、prev()
、printStatus()
) - ConcreteState(具体状态):实现特定状态下的行为,如
OrderedState
、DeliveredState
关键点:✅ Context 不关心具体状态,只负责转发请求给当前 State 实例
4. 代码实现
4.1 上下文类:Package
public class Package {
private PackageState state = new OrderedState();
// getter and setter
public PackageState getState() {
return state;
}
public void setState(PackageState state) {
this.state = state;
}
// 委托给状态对象处理
public void previousState() {
state.prev(this);
}
public void nextState() {
state.next(this);
}
public void printStatus() {
state.printStatus();
}
}
⚠️ 注意:
next()
和prev()
方法接收this
(即 Package 实例),以便状态类可以修改上下文的状态。
4.2 状态接口:PackageState
public interface PackageState {
void next(Package pkg);
void prev(Package pkg);
void printStatus();
}
统一契约,所有具体状态必须实现。
4.3 具体状态实现
OrderedState(已下单)
public class OrderedState implements PackageState {
@Override
public void next(Package pkg) {
pkg.setState(new DeliveredState());
}
@Override
public void prev(Package pkg) {
System.out.println("The package is in its root state.");
}
@Override
public void printStatus() {
System.out.println("Package ordered, not delivered to the office yet.");
}
}
✅ 初始状态,无法再往前
DeliveredState(已发货)
public class DeliveredState implements PackageState {
@Override
public void next(Package pkg) {
pkg.setState(new ReceivedState());
}
@Override
public void prev(Package pkg) {
pkg.setState(new OrderedState());
}
@Override
public void printStatus() {
System.out.println("Package delivered to post office, not received yet.");
}
}
✅ 可进可退,状态流转中枢
ReceivedState(已签收)
public class ReceivedState implements PackageState {
@Override
public void next(Package pkg) {
System.out.println("This package is already received by a client.");
}
@Override
public void prev(Package pkg) {
pkg.setState(new DeliveredState());
}
@Override
public void printStatus() {
System.out.println("Package was received by client.");
}
}
✅ 终止状态,只能回退
5. 测试验证
5.1 单元测试:状态流转
@Test
public void givenNewPackage_whenPackageReceived_thenStateReceived() {
Package pkg = new Package();
assertTrue(pkg.getState() instanceof OrderedState);
pkg.nextState();
assertTrue(pkg.getState() instanceof DeliveredState);
pkg.nextState();
assertTrue(pkg.getState() instanceof ReceivedState);
}
✅ 验证正向流转:Ordered → Delivered → Received
@Test
public void givenDeliveredPackage_whenPrevState_thenStateOrdered() {
Package pkg = new Package();
pkg.setState(new DeliveredState());
pkg.previousState();
assertTrue(pkg.getState() instanceof OrderedState);
}
✅ 验证回退:Delivered → Ordered
5.2 主函数演示:行为变化
public class StateDemo {
public static void main(String[] args) {
Package pkg = new Package();
pkg.printStatus(); // Ordered
pkg.nextState();
pkg.printStatus(); // Delivered
pkg.nextState();
pkg.printStatus(); // Received
pkg.nextState();
pkg.printStatus(); // 已签收提示
}
}
输出结果:
Package ordered, not delivered to the office yet.
Package delivered to post office, not received yet.
Package was received by client.
This package is already received by a client.
Package was received by client.
✅ 同一个
printStatus()
调用,输出不同 —— 行为随状态改变!
6. 缺点与注意事项
虽然 State 模式很优雅,但也有些坑要注意:
- ⚠️ 状态之间强耦合:每个状态类需要知道下一个状态是谁,比如
next()
中 new 下一个状态实例 - ⚠️ 类爆炸风险:状态多时,类数量线性增长(但总比 if/else 好维护)
- ⚠️ 不适合频繁变化的状态逻辑:如果状态跳转规则特别复杂,建议考虑 Spring State Machine
💡 小建议:若状态跳转逻辑复杂,可用配置表或状态机引擎解耦,避免硬编码。
7. State vs Strategy 模式对比
这两个模式 UML 结构几乎一样,但语义完全不同:
对比项 | State 模式 | Strategy 模式 |
---|---|---|
目的 | 对象行为随状态改变 | 封装可互换的算法 |
行为差异 | 完全不同的逻辑 | 同一目标,不同实现(如不同排序算法) |
切换控制 | 状态内部自动流转(如 FSM) | 客户端显式切换策略 |
典型场景 | 订单状态、审批流程 | 支付方式、压缩算法 |
✅ 简单记:State 是“状态驱动”,Strategy 是“策略可插拔”
8. 总结
State 模式适合用于:
- ✅ 有明确状态流转的业务(如订单、审批、游戏角色状态)
- ✅ 想干掉满屏 if/else 的代码洁癖者
- ✅ 需要遵循开闭原则,方便扩展新状态
核心价值:
- ✅ 行为与状态解耦
- ✅ 单一职责落地
- ✅ 易测试、易维护
🚀 进阶建议:对于大型项目或复杂状态机,可考虑使用 Spring State Machine,支持事件驱动、持久化、嵌套状态等高级特性。
完整示例代码已上传至 GitHub:https://github.com/yourname/tutorials/tree/master/design-patterns/state