一、状态模式核心定义

状态模式是行为型设计模式的一种,核心目的是:

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

简单来说:将对象的每一种状态封装成独立的类,对象的行为由当前状态决定;状态切换时,对象会委托给对应状态类处理,而非通过大量 if/else 或 switch 判断

核心解决的问题
  1. 消除状态判断分支:避免在一个类中用大量 if/else/switch 判断不同状态的行为(如订单类中判断 “已创建 / 已支付 / 已发货 / 已取消” 的逻辑);
  2. 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
  3. 状态切换可控:状态的转换规则集中管理,可灵活扩展新状态;
  4. 行为与状态解耦:对象无需知道状态的具体实现,只需委托状态类处理行为。
生活类比
  • 场景 1:电梯运行状态
    • 环境类(Context):电梯本身;
    • 状态类:停止状态、运行状态、开门状态、关门状态;
    • 核心:电梯的行为(开门、关门、上行、下行)由当前状态决定 —— 开门状态下无法上行,停止状态下才能开门,运行状态下只能下行 / 上行。
  • 场景 2:订单状态流转
    • 环境类:订单对象;
    • 状态类:已创建、已支付、已发货、已收货、已取消;
    • 核心:订单的行为(支付、发货、取消)由当前状态决定 —— 已创建状态可支付,已支付状态可发货 / 取消,已发货状态不可取消。
  • 场景 3:游戏角色状态
    • 环境类:游戏角色;
    • 状态类:正常状态、攻击状态、防御状态、死亡状态;
    • 核心:角色的行为(攻击、防御、移动)由当前状态决定 —— 死亡状态下无法攻击 / 移动,防御状态下伤害减免。
标准角色
角色职责类比(订单场景)代码定位
环境类(Context)维护当前状态,委托当前状态处理行为;提供状态切换的入口订单对象(持有当前状态,调用状态行为)业务核心对象(如OrderElevator
抽象状态(State)定义所有状态的通用行为接口(如订单的支付、取消、发货方法)订单状态接口(pay ()、cancel ()、ship ())接口 / 抽象类
具体状态(ConcreteState)实现抽象状态接口,封装对应状态的行为;定义状态转换规则(如已创建状态支付后切换为已支付状态)已创建状态、已支付状态、已取消状态各状态的实现类
核心 UML 类图

二、订单状态流转

以 “订单状态流转” 为例,实现状态模式的核心逻辑 —— 覆盖所有核心角色,消除 if/else 判断,通过状态类封装不同状态的行为。

1. 步骤 1:定义抽象状态(订单状态接口)
/**
 * 抽象状态:订单状态接口(定义所有状态的通用行为)
 */
public interface OrderState {
    /**
     * 支付订单
     */
    void pay(OrderContext context);

    /**
     * 取消订单
     */
    void cancel(OrderContext context);

    /**
     * 发货订单
     */
    void ship(OrderContext context);

    /**
     * 获取当前状态名称
     */
    String getStateName();
}
2. 步骤 2:实现具体状态(各订单状态)
/**
 * 具体状态1:已创建状态(初始状态)
 */
public class CreatedState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        // 已创建状态可支付,支付后切换为已支付状态
        System.out.println("【已创建状态】订单支付成功,状态切换为已支付");
        context.setState(new PaidState());
    }

    @Override
    public void cancel(OrderContext context) {
        // 已创建状态可取消,取消后切换为已取消状态
        System.out.println("【已创建状态】订单取消成功,状态切换为已取消");
        context.setState(new CancelledState());
    }

    @Override
    public void ship(OrderContext context) {
        // 已创建状态不可发货
        System.out.println("【已创建状态】订单未支付,无法发货");
    }

    @Override
    public String getStateName() {
        return "已创建";
    }
}

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

    @Override
    public void cancel(OrderContext context) {
        // 已支付状态可取消,取消后切换为已取消状态
        System.out.println("【已支付状态】订单取消成功,状态切换为已取消");
        context.setState(new CancelledState());
    }

    @Override
    public void ship(OrderContext context) {
        // 已支付状态可发货,发货后切换为已发货状态
        System.out.println("【已支付状态】订单发货成功,状态切换为已发货");
        context.setState(new ShippedState());
    }

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

/**
 * 具体状态3:已发货状态
 */
public class ShippedState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("【已发货状态】订单已发货,无需支付");
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("【已发货状态】订单已发货,无法取消");
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("【已发货状态】订单已发货,无需重复发货");
    }

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

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

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

    @Override
    public void ship(OrderContext context) {
        System.out.println("【已取消状态】订单已取消,无法发货");
    }

    @Override
    public String getStateName() {
        return "已取消";
    }
}
3. 步骤 3:实现环境类(订单上下文)
/**
 * 环境类:订单上下文(维护当前状态,委托状态处理行为)
 */
public class OrderContext {
    // 订单ID
    private String orderId;
    // 当前状态(核心:委托该状态处理所有行为)
    private OrderState currentState;

    /**
     * 初始化订单,默认状态为已创建
     */
    public OrderContext(String orderId) {
        this.orderId = orderId;
        this.currentState = new CreatedState(); // 初始状态
        System.out.println("【订单上下文】订单创建成功 | 订单ID:" + orderId + ",初始状态:" + currentState.getStateName());
    }

    /**
     * 切换状态(供状态类调用)
     */
    public void setState(OrderState state) {
        this.currentState = state;
        System.out.println("【订单上下文】订单状态更新 | 订单ID:" + orderId + ",当前状态:" + currentState.getStateName());
    }

    /**
     * 支付订单(委托当前状态处理)
     */
    public void pay() {
        System.out.println("\n【订单上下文】发起支付请求 | 订单ID:" + orderId);
        currentState.pay(this);
    }

    /**
     * 取消订单(委托当前状态处理)
     */
    public void cancel() {
        System.out.println("\n【订单上下文】发起取消请求 | 订单ID:" + orderId);
        currentState.cancel(this);
    }

    /**
     * 发货订单(委托当前状态处理)
     */
    public void ship() {
        System.out.println("\n【订单上下文】发起发货请求 | 订单ID:" + orderId);
        currentState.ship(this);
    }

    // Getter
    public String getOrderId() {
        return orderId;
    }

    public String getCurrentStateName() {
        return currentState.getStateName();
    }
}
4. 客户端(测试订单状态流转)
/**
 * 客户端:测试订单状态流转(状态模式)
 */
public class StateClient {
    public static void main(String[] args) {
        // 1. 创建订单上下文(初始状态:已创建)
        OrderContext order = new OrderContext("O20240314001");

        // 2. 测试1:已创建状态下支付
        order.pay();

        // 3. 测试2:已支付状态下发货
        order.ship();

        // 4. 测试3:已发货状态下取消(不允许)
        order.cancel();

        // 5. 重新创建订单,测试取消
        System.out.println("\n======= 测试新订单取消 =======");
        OrderContext order2 = new OrderContext("O20240314002");
        order2.cancel();

        // 6. 已取消状态下支付(不允许)
        order2.pay();
    }
}
输出结果
【订单上下文】订单创建成功 | 订单ID:O20240314001,初始状态:已创建

【订单上下文】发起支付请求 | 订单ID:O20240314001
【已创建状态】订单支付成功,状态切换为已支付
【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已支付

【订单上下文】发起发货请求 | 订单ID:O20240314001
【已支付状态】订单发货成功,状态切换为已发货
【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已发货

【订单上下文】发起取消请求 | 订单ID:O20240314001
【已发货状态】订单已发货,无法取消

======= 测试新订单取消 =======
【订单上下文】订单创建成功 | 订单ID:O20240314002,初始状态:已创建

【订单上下文】发起取消请求 | 订单ID:O20240314002
【已创建状态】订单取消成功,状态切换为已取消
【订单上下文】订单状态更新 | 订单ID:O20240314002,当前状态:已取消

【订单上下文】发起支付请求 | 订单ID:O20240314002
【已取消状态】订单已取消,无法支付

三、Spring 实战版(订单状态机 + 注解驱动)

在业务开发中,状态模式最核心的实战场景是结合 Spring 实现可配置的订单状态机—— 通过 Spring 的依赖注入管理状态类,通过注解简化状态切换,支持动态扩展新状态。

1. 依赖准备(Spring Boot)
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>3.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- Spring状态机(可选,简化状态流转配置) -->
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-core</artifactId>
        <version>4.0.1</version>
    </dependency>
</dependencies>
2. 核心模型定义
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 订单状态枚举(统一状态标识)
 */
@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
    CREATED("CREATED", "已创建"),
    PAID("PAID", "已支付"),
    SHIPPED("SHIPPED", "已发货"),
    RECEIVED("RECEIVED", "已收货"),
    CANCELLED("CANCELLED", "已取消");

    private final String code;
    private final String desc;
}

/**
 * 订单事件枚举(触发状态切换的事件)
 */
@Getter
@AllArgsConstructor
public enum OrderEventEnum {
    PAY("PAY", "支付"),
    CANCEL("CANCEL", "取消"),
    SHIP("SHIP", "发货"),
    RECEIVE("RECEIVE", "确认收货");

    private final String code;
    private final String desc;
}
3. 抽象状态(Spring Bean 接口)
import org.springframework.stereotype.Component;

/**
 * 抽象状态:订单状态接口(标记为Spring组件,便于注入)
 */
public interface OrderState {
    /**
     * 处理事件,返回新状态(核心:状态转换规则)
     */
    OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event);

    /**
     * 获取当前状态枚举
     */
    OrderStatusEnum getStatus();
}
4. 具体状态(Spring Bean 实现)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 具体状态1:已创建状态(Spring Bean)
 */
@Slf4j
@Component
public class CreatedState implements OrderState {
    @Override
    public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
        switch (event) {
            case PAY:
                log.info("【已创建状态】处理支付事件 | 订单ID:{}", context.getOrderId());
                return OrderStatusEnum.PAID; // 支付后切换为已支付
            case CANCEL:
                log.info("【已创建状态】处理取消事件 | 订单ID:{}", context.getOrderId());
                return OrderStatusEnum.CANCELLED; // 取消后切换为已取消
            default:
                log.warn("【已创建状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
                return context.getCurrentStatus(); // 不切换状态
        }
    }

    @Override
    public OrderStatusEnum getStatus() {
        return OrderStatusEnum.CREATED;
    }
}

/**
 * 具体状态2:已支付状态(Spring Bean)
 */
@Slf4j
@Component
public class PaidState implements OrderState {
    @Override
    public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
        switch (event) {
            case SHIP:
                log.info("【已支付状态】处理发货事件 | 订单ID:{}", context.getOrderId());
                return OrderStatusEnum.SHIPPED; // 发货后切换为已发货
            case CANCEL:
                log.info("【已支付状态】处理取消事件 | 订单ID:{}", context.getOrderId());
                return OrderStatusEnum.CANCELLED; // 取消后切换为已取消
            default:
                log.warn("【已支付状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
                return context.getCurrentStatus();
        }
    }

    @Override
    public OrderStatusEnum getStatus() {
        return OrderStatusEnum.PAID;
    }
}

/**
 * 具体状态3:已发货状态(Spring Bean)
 */
@Slf4j
@Component
public class ShippedState implements OrderState {
    @Override
    public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
        switch (event) {
            case RECEIVE:
                log.info("【已发货状态】处理确认收货事件 | 订单ID:{}", context.getOrderId());
                return OrderStatusEnum.RECEIVED; // 确认收货后切换为已收货
            default:
                log.warn("【已发货状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
                return context.getCurrentStatus();
        }
    }

    @Override
    public OrderStatusEnum getStatus() {
        return OrderStatusEnum.SHIPPED;
    }
}

/**
 * 具体状态4:已取消状态(Spring Bean)
 */
@Slf4j
@Component
public class CancelledState implements OrderState {
    @Override
    public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
        log.warn("【已取消状态】不支持任何事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
        return context.getCurrentStatus(); // 已取消状态不切换
    }

    @Override
    public OrderStatusEnum getStatus() {
        return OrderStatusEnum.CANCELLED;
    }
}
5. 环境类(Spring 管理的订单上下文)
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 环境类:订单上下文(Spring Bean,管理状态映射和当前状态)
 */
@Data
@Component
public class OrderContext {
    private String orderId;
    private OrderStatusEnum currentStatus;

    // 状态映射:状态枚举 → 状态实例(Spring注入所有状态类)
    private final Map<OrderStatusEnum, OrderState> stateMap = new ConcurrentHashMap<>();

    /**
     * 构造函数:注入所有OrderState实现类,初始化状态映射
     */
    @Autowired
    public OrderContext(Map<String, OrderState> stateBeans) {
        // 将Spring容器中的所有OrderState Bean放入映射
        for (OrderState state : stateBeans.values()) {
            stateMap.put(state.getStatus(), state);
        }
        log.info("【订单上下文】初始化状态映射,共加载{}个状态类", stateMap.size());
    }

    /**
     * 初始化订单
     */
    public void init(String orderId) {
        this.orderId = orderId;
        this.currentStatus = OrderStatusEnum.CREATED; // 初始状态
        log.info("【订单上下文】订单初始化 | 订单ID:{},初始状态:{}", orderId, currentStatus.getDesc());
    }

    /**
     * 触发事件(核心:委托当前状态处理事件,切换状态)
     */
    public void triggerEvent(OrderEventEnum event) {
        log.info("\n【订单上下文】触发事件 | 订单ID:{},当前状态:{},事件:{}",
                orderId, currentStatus.getDesc(), event.getDesc());

        // 获取当前状态实例
        OrderState currentState = stateMap.get(currentStatus);
        if (currentState == null) {
            log.error("【订单上下文】未找到状态实例 | 状态:{}", currentStatus.getDesc());
            return;
        }

        // 委托当前状态处理事件,获取新状态
        OrderStatusEnum newStatus = currentState.handleEvent(this, event);
        if (!newStatus.equals(currentStatus)) {
            this.currentStatus = newStatus;
            log.info("【订单上下文】状态切换成功 | 订单ID:{},新状态:{}", orderId, newStatus.getDesc());
        }
    }
}
6. 订单服务(业务入口)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 订单服务(业务入口,封装状态机调用)
 */
@Slf4j
@Service
public class OrderService {
    @Resource
    private OrderContext orderContext;

    /**
     * 创建订单
     */
    public void createOrder(String orderId) {
        orderContext.init(orderId);
    }

    /**
     * 支付订单
     */
    public void payOrder(String orderId) {
        orderContext.setOrderId(orderId); // 绑定订单ID
        orderContext.triggerEvent(OrderEventEnum.PAY);
    }

    /**
     * 取消订单
     */
    public void cancelOrder(String orderId) {
        orderContext.setOrderId(orderId);
        orderContext.triggerEvent(OrderEventEnum.CANCEL);
    }

    /**
     * 发货订单
     */
    public void shipOrder(String orderId) {
        orderContext.setOrderId(orderId);
        orderContext.triggerEvent(OrderEventEnum.SHIP);
    }

    /**
     * 确认收货
     */
    public void receiveOrder(String orderId) {
        orderContext.setOrderId(orderId);
        orderContext.triggerEvent(OrderEventEnum.RECEIVE);
    }
}
7. 客户端(Spring Boot 测试)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * 客户端:测试Spring版订单状态机
 */
@SpringBootApplication
public class SpringStateDemoApplication {
    public static void main(String[] args) {
        // 1. 启动Spring容器(自动加载所有状态类和上下文)
        ConfigurableApplicationContext context = SpringApplication.run(SpringStateDemoApplication.class, args);
        OrderService orderService = context.getBean(OrderService.class);

        // 2. 测试订单状态流转
        System.out.println("======= 测试订单状态流转 =======");
        String orderId = "O20240314003";

        // 创建订单(初始状态:已创建)
        orderService.createOrder(orderId);

        // 支付订单(已创建→已支付)
        orderService.payOrder(orderId);

        // 发货订单(已支付→已发货)
        orderService.shipOrder(orderId);

        // 确认收货(已发货→已收货)
        orderService.receiveOrder(orderId);

        // 尝试取消已收货订单(不支持)
        orderService.cancelOrder(orderId);

        context.close();
    }
}
输出结果
【订单上下文】初始化状态映射,共加载4个状态类

======= 测试订单状态流转 =======
【订单上下文】订单初始化 | 订单ID:O20240314003,初始状态:已创建

【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已创建,事件:支付
【已创建状态】处理支付事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已支付

【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已支付,事件:发货
【已支付状态】处理发货事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已发货

【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已发货,事件:确认收货
【已发货状态】处理确认收货事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已收货

【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已收货,事件:取消
【已收货状态】不支持事件 | 订单ID:O20240314003,事件:取消

四、状态模式的核心特点与适用场景

优点
  1. 消除分支判断:替代大量 if/else/switch,代码更简洁、易维护;
  2. 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
  3. 状态扩展灵活:新增状态只需实现抽象状态接口,无需修改原有代码(开闭原则);
  4. 状态切换可控:状态转换规则集中在状态类中,便于管理和修改;
  5. 行为与状态解耦:环境类无需知道状态的具体实现,只需委托状态类处理。
缺点
  1. 类数量膨胀:每种状态对应一个类,状态较多时会产生大量状态类(如 10 种状态 = 10 个状态类);
  2. 状态逻辑分散:状态转换规则分散在各状态类中,复杂状态流转时难以全局把控;
  3. 依赖状态类:环境类依赖所有状态类,增加了类之间的关联;
  4. 简单场景冗余:简单的状态判断(如 2-3 种状态)使用状态模式会增加复杂度。
适用场景
  1. 对象行为随状态变化:如订单状态流转、电梯运行状态、游戏角色状态;
  2. 存在大量状态判断分支:类中包含大量 if/else/switch 判断不同状态的行为;
  3. 状态流转规则明确:状态之间的转换规则清晰,且可能需要扩展;
  4. 工作流 / 审批流程:如请假审批(待审批 / 审批中 / 已通过 / 已驳回)、合同审批;
  5. 有限状态机(FSM):如通信协议状态、设备运行状态、状态机框架开发。

五、JDK/ Spring 中的原生应用

状态模式在框架中主要用于状态驱动的行为控制,以下是核心场景:

1. Spring StateMachine(Spring 状态机)
  • 抽象状态:State
  • 具体状态:自定义State实现;
  • 环境类:StateMachine
  • 核心逻辑:基于状态模式实现可配置的状态机,支持状态流转、事件触发、监听器扩展。
2. Java 线程状态(Thread.State)
  • 抽象状态:Thread.State(枚举);
  • 具体状态:NEWRUNNABLEBLOCKEDWAITINGTERMINATED
  • 环境类:Thread
  • 核心逻辑:线程的行为(start ()、wait ()、notify ())由当前状态决定,如 NEW 状态的线程只能 start (),TERMINATED 状态的线程无法再次 start ()。
3. JDBC 连接状态(Connection)
  • 抽象状态:连接状态(未连接 / 已连接 / 已关闭);
  • 具体状态:ClosedStateConnectedStateIdleState
  • 环境类:Connection
  • 核心逻辑:连接的行为(execute ()、close ()、commit ())由当前状态决定,如已关闭的连接无法执行 SQL。
4. Servlet 生命周期(Servlet)
  • 抽象状态:Servlet生命周期状态(初始化 / 运行 / 销毁);
  • 具体状态:InitStateServiceStateDestroyState
  • 环境类:HttpServlet
  • 核心逻辑:Servlet 的行为(init ()、service ()、destroy ())由当前生命周期状态决定。
5. Netty Channel 状态(ChannelState)
  • 抽象状态:ChannelState
  • 具体状态:CHANNEL_OPENCHANNEL_ACTIVECHANNEL_INACTIVECHANNEL_CLOSED
  • 环境类:Channel
  • 核心逻辑:Channel 的 I/O 行为(read ()、write ())由当前状态决定,如 CLOSED 状态的 Channel 无法读写。

六、状态模式 vs 策略模式(易混淆点)

状态模式和策略模式的类结构几乎一致,但设计意图完全不同:

维度状态模式策略模式
核心目的处理状态驱动的行为变化,状态由对象内部决定处理算法 / 策略的替换,策略由外部传入
状态 / 策略切换内部自动切换(如订单支付后自动切换为已支付状态)外部手动切换(如选择不同的排序算法)
状态 / 策略关系状态之间有明确的流转规则(如已创建→已支付)策略之间相互独立,无依赖关系
关注点状态与行为的绑定算法的灵活替换
典型场景订单状态、电梯状态、生命周期管理排序算法、支付方式、校验规则

总结

  1. 状态模式的核心是将对象的每种状态封装为独立类,对象行为由当前状态决定,通过状态类委托处理而非分支判断
  2. 核心角色包括环境类(维护当前状态)、抽象状态(定义行为接口)、具体状态(封装状态行为和转换规则);
  3. 状态模式适用于对象行为随状态变化、存在大量状态判断分支的场景,如订单状态流转、工作流审批;
  4. 状态模式与策略模式结构相似但意图不同:状态模式关注内部状态驱动的行为变化,策略模式关注外部策略的灵活替换。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐