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 结构图

UML diagram of state design pattern

核心角色说明:

  • Context(上下文):如 Package,持有当前状态对象,所有行为委托给状态实现
  • State(状态接口):定义状态共有的行为(如 next()prev()printStatus()
  • ConcreteState(具体状态):实现特定状态下的行为,如 OrderedStateDeliveredState

关键点:✅ 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


原始标题:State Design Pattern in Java