Skip to content

状态模式 (State)

一、定义

一句话概括:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

官方定义:Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

二、解决的问题

2.1 问题场景

很多对象的行为取决于其当前状态。例如:

  • 订单:待支付、已支付、已发货、已完成、已取消 — 不同状态下操作不同
  • 电梯:静止、上升、下降、开门、关门
  • TCP 连接:CLOSED、LISTEN、SYN_SENT、ESTABLISHED 等
  • 游戏角色:站立、行走、跑步、跳跃、攻击

2.2 不用状态模式会怎样?

java
// 反例:大量 if-else 判断状态
class Order {
    private String status;

    public void pay() {
        if ("CREATED".equals(status)) {
            status = "PAID";
            System.out.println("支付成功");
        } else if ("PAID".equals(status)) {
            System.out.println("已支付,无需重复支付");
        } else if ("CANCELLED".equals(status)) {
            System.out.println("订单已取消,无法支付");
        }
        // 每个方法都需要大量 if-else
    }

    public void ship() {
        if ("PAID".equals(status)) {
            status = "SHIPPED";
        } else {
            System.out.println("当前状态无法发货");
        }
    }
    // 新增状态需要修改所有方法,违反开闭原则
}

问题:

  1. 大量 if-else 或 switch-case,代码难以维护
  2. 新增状态需要修改所有相关方法,违反开闭原则
  3. 状态转换逻辑分散在各处,难以追踪

三、结构

3.1 角色组成

角色说明
Context(上下文)持有当前状态,定义客户端感兴趣的接口
State(抽象状态)定义与 Context 状态相关的行为接口
ConcreteState(具体状态)实现 State 接口,定义该状态下的具体行为

3.2 类图(ASCII)

┌──────────────┐         ┌──────────────┐
│   Context    │────────►│    State     │
├──────────────┤         ├──────────────┤
│ - state      │         │ + handle()   │
│ + request()  │         └──────┬───────┘
│ + setState() │                │
└──────────────┘         ┌──────┴──────┐
                         │             │
                         ▼             ▼
                  ┌──────────┐  ┌──────────┐
                  │Concrete  │  │Concrete  │
                  │StateA    │  │StateB    │
                  └──────────┘  └──────────┘

四、代码实现

4.1 基础实现

java
// ==================== 抽象状态 ====================
interface OrderState {
    void pay(OrderContext context);
    void ship(OrderContext context);
    void cancel(OrderContext context);
    String getStatusName();
}

// ==================== 具体状态:待支付 ====================
class CreatedState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("支付成功,订单状态变更为:已支付");
        context.setState(new PaidState());
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("错误:待支付订单无法发货");
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("订单已取消");
        context.setState(new CancelledState());
    }

    @Override
    public String getStatusName() { return "待支付"; }
}

// ==================== 具体状态:已支付 ====================
class PaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("订单已支付,无需重复支付");
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("发货成功,订单状态变更为:已发货");
        context.setState(new ShippedState());
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("订单已取消,将发起退款");
        context.setState(new CancelledState());
    }

    @Override
    public String getStatusName() { return "已支付"; }
}

// ==================== 具体状态:已发货 ====================
class ShippedState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("错误:已发货订单无法支付");
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("错误:已发货订单无法重复发货");
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("订单已取消,将发起退货退款");
        context.setState(new CancelledState());
    }

    @Override
    public String getStatusName() { return "已发货"; }
}

// ==================== 具体状态:已取消 ====================
class CancelledState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("错误:已取消订单无法支付");
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("错误:已取消订单无法发货");
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("订单已取消,无需重复操作");
    }

    @Override
    public String getStatusName() { return "已取消"; }
}

// ==================== 上下文 ====================
class OrderContext {
    private OrderState state;
    private Long orderId;

    public OrderContext(Long orderId) {
        this.orderId = orderId;
        this.state = new CreatedState(); // 初始状态
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public void pay() { state.pay(this); }
    public void ship() { state.ship(this); }
    public void cancel() { state.cancel(this); }

    public String getCurrentStatus() {
        return state.getStatusName();
    }
}

// ==================== 客户端 ====================
public class StateDemo {
    public static void main(String[] args) {
        OrderContext order = new OrderContext(1001L);
        System.out.println("当前状态:" + order.getCurrentStatus());

        order.pay();   // 支付成功 → 已支付
        order.ship();  // 发货成功 → 已发货
        order.pay();   // 错误:已发货订单无法支付
        System.out.println("当前状态:" + order.getCurrentStatus());
    }
}

4.2 进阶实现

4.2.1 状态模式 vs 策略模式

维度状态模式策略模式
关注点对象状态变化导致行为变化算法/策略的可替换性
状态切换由 Context 自身或 State 触发由客户端主动选择
生命周期State 之间相互感知,可互相切换策略之间互不感知,独立存在
客户端感知客户端通常不直接选择状态客户端主动选择策略
典型场景订单状态机、开关状态支付方式、排序算法
java
// 状态模式:State 之间可以互相切换
class TVOnState implements TVState {
    public void pressPower(TVContext tv) {
        tv.setState(new TVOffState()); // 状态自己决定下一个状态
    }
}

// 策略模式:策略之间互不关心
class AlipayStrategy implements PaymentStrategy {
    public void pay() { /* 支付逻辑 */ }
    // 不会主动切换到 WechatPayStrategy
}

4.2.2 Spring StateMachine

java
// Spring StateMachine 配置示例
@Configuration
@EnableStateMachine
class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {

    @Override
    public void configure(StateMachineStateConfigurer<String, String> states)
            throws Exception {
        states.withStates()
            .initial("CREATED")
            .state("PAID")
            .state("SHIPPED")
            .state("COMPLETED")
            .end("CANCELLED");
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<String, String> transitions)
            throws Exception {
        transitions
            .withExternal()
                .source("CREATED").target("PAID").event("PAY")
            .and()
            .withExternal()
                .source("PAID").target("SHIPPED").event("SHIP")
            .and()
            .withExternal()
                .source("SHIPPED").target("COMPLETED").event("COMPLETE")
            .and()
            .withExternal()
                .source("CREATED").target("CANCELLED").event("CANCEL")
            .and()
            .withExternal()
                .source("PAID").target("CANCELLED").event("CANCEL");
    }
}

4.3 生产级实现

Spring Boot 订单状态机

java
// ==================== 枚举状态 ====================
enum OrderStatus {
    CREATED, PAID, SHIPPED, COMPLETED, CANCELLED, REFUNDING, REFUNDED
}

// ==================== 抽象状态处理器 ====================
interface OrderStateHandler {
    OrderStatus getStatus();
    void handle(OrderEntity order);
    Set<OrderStatus> getAllowedTransitions();
}

// ==================== 具体状态处理器 ====================
@Component
class CreatedStateHandler implements OrderStateHandler {
    @Override
    public OrderStatus getStatus() { return OrderStatus.CREATED; }

    @Override
    public void handle(OrderEntity order) {
        // 状态进入时的处理逻辑
        System.out.println("订单创建: " + order.getId());
    }

    @Override
    public Set<OrderStatus> getAllowedTransitions() {
        return Set.of(OrderStatus.PAID, OrderStatus.CANCELLED);
    }
}

@Component
class PaidStateHandler implements OrderStateHandler {
    @Override
    public OrderStatus getStatus() { return OrderStatus.PAID; }

    @Override
    public void handle(OrderEntity order) {
        System.out.println("订单支付成功: " + order.getId());
    }

    @Override
    public Set<OrderStatus> getAllowedTransitions() {
        return Set.of(OrderStatus.SHIPPED, OrderStatus.CANCELLED, OrderStatus.REFUNDING);
    }
}

// ==================== 状态机服务 ====================
@Service
class OrderStateMachineService {
    private final Map<OrderStatus, OrderStateHandler> handlers = new HashMap<>();

    @Autowired
    public OrderStateMachineService(List<OrderStateHandler> handlerList) {
        for (OrderStateHandler handler : handlerList) {
            handlers.put(handler.getStatus(), handler);
        }
    }

    public void transition(OrderEntity order, OrderStatus targetStatus) {
        OrderStatus currentStatus = order.getStatus();
        OrderStateHandler handler = handlers.get(currentStatus);

        if (handler == null) {
            throw new IllegalStateException("未知状态: " + currentStatus);
        }

        if (!handler.getAllowedTransitions().contains(targetStatus)) {
            throw new IllegalStateException(
                "不允许从 " + currentStatus + " 转换到 " + targetStatus);
        }

        order.setStatus(targetStatus);
        handlers.get(targetStatus).handle(order);
    }
}

// ==================== Controller ====================
@RestController
@RequestMapping("/api/orders")
class OrderStateController {
    @Autowired private OrderStateMachineService stateMachine;
    @Autowired private OrderRepository orderRepository;

    @PostMapping("/{id}/pay")
    public OrderEntity pay(@PathVariable Long id) {
        OrderEntity order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        stateMachine.transition(order, OrderStatus.PAID);
        return orderRepository.save(order);
    }

    @PostMapping("/{id}/ship")
    public OrderEntity ship(@PathVariable Long id) {
        OrderEntity order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        stateMachine.transition(order, OrderStatus.SHIPPED);
        return orderRepository.save(order);
    }
}

五、优缺点

优点

  1. 消除 if-else:将状态判断逻辑分散到各个状态类中,代码更清晰
  2. 符合开闭原则:新增状态只需新增一个状态类,无需修改现有代码
  3. 状态转换显式化:状态转换逻辑集中在状态类中,易于追踪
  4. 避免状态不一致:状态类可以控制合法的状态转换

缺点

  1. 类数量增加:每个状态都需要一个类
  2. 状态类相互依赖:状态类之间需要知道对方的存在
  3. Context 职责增加:Context 需要维护状态转换逻辑

六、适用场景

  1. 订单系统:订单状态的流转(创建 → 支付 → 发货 → 完成)
  2. 工作流引擎:审批流程的状态流转
  3. 游戏开发:角色状态(站立、行走、跑步、攻击、死亡)
  4. 网络协议:TCP 连接状态机
  5. 电梯控制:电梯运行状态
  6. 自动售货机:投币、选择、出货、找零

七、JDK / Spring 框架中的实际应用

框架应用位置说明
SpringSpring StateMachine状态机框架
SpringLifecycle 接口组件的生命周期状态
JSFLifecycleJSF 请求处理生命周期
NettyChannelStateChannel 状态管理
JDKThread.State线程状态(NEW, RUNNABLE, WAITING 等)

八、与其他模式的关系

与策略模式(重点区别)

  • 结构相似:类图几乎一样,都是 Context 持有接口引用
  • 意图不同:状态模式关注"状态切换导致行为变化",策略模式关注"算法的可替换性"
  • 最核心区别:状态模式中,State 之间可以互相切换(知道彼此存在);策略模式中,策略之间互不感知

与享元模式

  • 状态对象通常是单例的,可以使用享元模式共享,减少对象创建

与责任链模式

  • 可以使用责任链模式处理状态之间的转换逻辑

九、面试常见问题

Q1:状态模式和策略模式有什么区别?

A:两者结构相似,但意图不同。状态模式中,状态对象知道彼此的存在,可以自行切换状态(如 CreatedStatepay() 后切换到 PaidState);策略模式中,策略对象互不感知,由客户端选择策略。状态模式关注"状态变化导致行为变化",策略模式关注"算法可替换"。

Q2:状态模式如何处理状态转换的合法性校验?

A:可以在每个状态类中维护一个允许转换的目标状态集合(getAllowedTransitions()),在 Context 或状态机中调用 transition() 前进行校验,不合法则抛出异常。也可以使用 Spring StateMachine 等框架,通过配置声明式地定义合法状态转换。

Q3:状态模式中状态对象应该使用单例还是每次创建?

A:如果状态对象是无状态的(不持有实例变量),应该使用单例,通过享元模式共享,减少内存开销。如果状态对象持有状态相关的数据,则需要每次创建新实例。大多数情况下,状态对象是无状态的,建议使用单例。

Q4:如果状态非常多(如 50+ 个状态),状态模式还适用吗?

A:如果状态非常多,可以考虑使用状态表(State Table)替代状态模式,用一个二维数组或 Map 存储状态转换关系。或者使用 Spring StateMachine 等状态机框架,通过声明式配置管理状态转换。状态模式更适合状态数量适中(5-15 个)的场景。