设计模式之十六:状态模式(State Pattern)详解 -优雅地管理对象状态,告别繁琐的条件判断

一、引言

在软件开发中,我们经常遇到这样的场景:同一个对象在不同状态下表现出不同的行为。传统的做法是使用大量的 if-else 或 switch-case 语句,但随着状态增多,代码会变得越来越难以维护。状态模式正是为解决这一问题而生。

本文将深入介绍状态模式的概念、实现方式,以及在Spring框架中的实际应用,帮助你掌握这一重要的设计模式。

二、什么是状态模式?

2.1 定义

状态模式(State Pattern)允许对象在内部状态改变时改变它的行为,对象看起来似乎修改了它的类。这是一种行为型设计模式。

2.2 核心思想

  • 将状态相关的行为封装到独立的状态类中
  • 通过委派将行为委托给当前状态对象处理
  • 状态转换可以由Context或State类管理

2.3 模式结构

复制代码
┌─────────────────┐      ┌─────────────────┐
│     Context     │      │      State      │
├─────────────────┤      ├─────────────────┤
│ - state: State  │─────▶│ + handle()      │
├─────────────────┤      └─────────────────┘
│ + request()     │              ▲
│ + setState()    │              │
└─────────────────┘     ┌────────┴────────┐
                        │                 │
              ┌─────────┴──────┐  ┌───────┴─────────┐
              │  ConcreteStateA│  │ ConcreteStateB │
              ├────────────────┤  ├─────────────────┤
              │ + handle()     │  │ + handle()      │
              └────────────────┘  └─────────────────┘

三、Java实战:订单状态管理系统

让我们通过一个完整的订单状态管理示例,深入理解状态模式的应用。

3.1 状态接口定义

java 复制代码
/**
 * 订单状态接口
 * 定义订单在不同状态下支持的操作
 */
public interface OrderState {
    /**
     * 支付操作
     * @param order 订单上下文
     */
    void pay(OrderContext order);
    
    /**
     * 发货操作
     * @param order 订单上下文
     */
    void ship(OrderContext order);
    
    /**
     * 确认收货操作
     * @param order 订单上下文
     */
    void confirm(OrderContext order);
    
    /**
     * 取消订单操作
     * @param order 订单上下文
     */
    void cancel(OrderContext order);
    
    /**
     * 获取状态名称
     * @return 状态名称
     */
    String getStateName();
}

3.2 具体状态实现

java 复制代码
/**
 * 待支付状态
 */
public class PendingPaymentState implements OrderState {
    @Override
    public void pay(OrderContext order) {
        System.out.println("✓ 支付成功,订单状态从待支付变为待发货");
        order.setState(new PendingShipmentState());
    }
    
    @Override
    public void ship(OrderContext order) {
        throw new IllegalStateException("✗ 订单未支付,不能发货");
    }
    
    @Override
    public void confirm(OrderContext order) {
        throw new IllegalStateException("✗ 订单未支付,不能确认收货");
    }
    
    @Override
    public void cancel(OrderContext order) {
        System.out.println("✓ 订单已取消");
        order.setState(new CancelledState());
    }
    
    @Override
    public String getStateName() {
        return "待支付";
    }
}

/**
 * 待发货状态
 */
public class PendingShipmentState implements OrderState {
    @Override
    public void pay(OrderContext order) {
        System.out.println("ℹ 订单已支付,不能重复支付");
    }
    
    @Override
    public void ship(OrderContext order) {
        System.out.println("✓ 发货成功,订单状态从待发货变为待收货");
        order.setState(new PendingReceiptState());
    }
    
    @Override
    public void confirm(OrderContext order) {
        throw new IllegalStateException("✗ 订单未发货,不能确认收货");
    }
    
    @Override
    public void cancel(OrderContext order) {
        System.out.println("✓ 订单已取消,退款处理中");
        order.setState(new CancelledState());
    }
    
    @Override
    public String getStateName() {
        return "待发货";
    }
}

/**
 * 待收货状态
 */
public class PendingReceiptState implements OrderState {
    @Override
    public void pay(OrderContext order) {
        System.out.println("ℹ 订单已完成支付");
    }
    
    @Override
    public void ship(OrderContext order) {
        System.out.println("ℹ 订单已发货,不能重复发货");
    }
    
    @Override
    public void confirm(OrderContext order) {
        System.out.println("✓ 确认收货成功,订单完成");
        order.setState(new CompletedState());
    }
    
    @Override
    public void cancel(OrderContext order) {
        System.out.println("ℹ 订单取消申请中,请联系客服");
    }
    
    @Override
    public String getStateName() {
        return "待收货";
    }
}

/**
 * 已取消状态
 */
public class CancelledState implements OrderState {
    @Override
    public void pay(OrderContext order) {
        System.out.println("✗ 订单已取消,不能支付");
    }
    
    @Override
    public void ship(OrderContext order) {
        System.out.println("✗ 订单已取消,不能发货");
    }
    
    @Override
    public void confirm(OrderContext order) {
        System.out.println("ℹ 订单已取消");
    }
    
    @Override
    public void cancel(OrderContext order) {
        System.out.println("ℹ 订单已处于取消状态");
    }
    
    @Override
    public String getStateName() {
        return "已取消";
    }
}

/**
 * 已完成状态
 */
public class CompletedState implements OrderState {
    @Override
    public void pay(OrderContext order) {
        System.out.println("ℹ 订单已完成,无需支付");
    }
    
    @Override
    public void ship(OrderContext order) {
        System.out.println("✗ 订单已完成,不能发货");
    }
    
    @Override
    public void confirm(OrderContext order) {
        System.out.println("ℹ 订单已完成");
    }
    
    @Override
    public void cancel(OrderContext order) {
        System.out.println("ℹ 订单已完成,如需退货请联系客服");
    }
    
    @Override
    public String getStateName() {
        return "已完成";
    }
}

3.3 上下文类实现

java 复制代码
/**
 * 订单上下文类
 * 维护当前状态,并将请求委托给状态对象处理
 */
public class OrderContext {
    private OrderState currentState;
    private String orderId;
    private String customerName;
    private BigDecimal amount;
    private LocalDateTime createTime;
    
    public OrderContext(String orderId, String customerName, BigDecimal amount) {
        this.orderId = orderId;
        this.customerName = customerName;
        this.amount = amount;
        this.createTime = LocalDateTime.now();
        // 初始状态为待支付
        this.currentState = new PendingPaymentState();
        printOrderInfo();
    }
    
    /**
     * 设置新状态
     */
    public void setState(OrderState state) {
        this.currentState = state;
        System.out.println("┌─────────────────────────────────────────┐");
        System.out.println("│ 订单[" + orderId + "]状态更新:" + currentState.getStateName());
        System.out.println("└─────────────────────────────────────────┘");
    }
    
    /**
     * 支付
     */
    public void pay() {
        System.out.println("▶ 执行支付操作...");
        try {
            currentState.pay(this);
        } catch (Exception e) {
            System.err.println("  " + e.getMessage());
        }
    }
    
    /**
     * 发货
     */
    public void ship() {
        System.out.println("▶ 执行发货操作...");
        try {
            currentState.ship(this);
        } catch (Exception e) {
            System.err.println("  " + e.getMessage());
        }
    }
    
    /**
     * 确认收货
     */
    public void confirm() {
        System.out.println("▶ 执行确认收货操作...");
        try {
            currentState.confirm(this);
        } catch (Exception e) {
            System.err.println("  " + e.getMessage());
        }
    }
    
    /**
     * 取消订单
     */
    public void cancel() {
        System.out.println("▶ 执行取消订单操作...");
        try {
            currentState.cancel(this);
        } catch (Exception e) {
            System.err.println("  " + e.getMessage());
        }
    }
    
    /**
     * 打印订单信息
     */
    private void printOrderInfo() {
        System.out.println("\n┌────────────── 订单信息 ──────────────┐");
        System.out.println("│ 订单号:" + orderId);
        System.out.println("│ 客户:" + customerName);
        System.out.println("│ 金额:¥" + amount);
        System.out.println("│ 状态:" + currentState.getStateName());
        System.out.println("└────────────────────────────────────┘\n");
    }
    
    public String getCurrentState() {
        return currentState.getStateName();
    }
}

3.4 客户端测试代码

java 复制代码
import java.math.BigDecimal;

/**
 * 客户端测试类
 */
public class StatePatternDemo {
    public static void main(String[] args) {
        System.out.println("========== 状态模式示例:订单管理系统 ==========\n");
        
        // 创建订单1:正常流程
        System.out.println("【场景1】正常购物流程");
        OrderContext order1 = new OrderContext("ORD001", "张三", new BigDecimal("299.00"));
        
        order1.pay();      // 待支付 -> 待发货
        order1.ship();     // 待发货 -> 待收货
        order1.confirm();  // 待收货 -> 已完成
        
        // 创建订单2:取消流程
        System.out.println("\n【场景2】支付后取消订单");
        OrderContext order2 = new OrderContext("ORD002", "李四", new BigDecimal("599.00"));
        
        order2.pay();      // 支付
        order2.cancel();   // 取消(退款)
        
        // 创建订单3:异常操作演示
        System.out.println("\n【场景3】异常操作演示");
        OrderContext order3 = new OrderContext("ORD003", "王五", new BigDecimal("199.00"));
        
        order3.ship();     // 错误:未支付不能发货
        order3.pay();      // 支付
        order3.pay();      // 重复支付(提示)
        order3.ship();     // 发货
        order3.ship();     // 重复发货(提示)
        order3.confirm();  // 确认收货
        
        // 创建订单4:已完成订单操作
        System.out.println("\n【场景4】已完成订单操作");
        OrderContext order4 = new OrderContext("ORD004", "赵六", new BigDecimal("899.00"));
        
        order4.pay();      // 支付
        order4.ship();     // 发货
        order4.confirm();  // 确认收货
        order4.cancel();   // 已完成订单取消(提示联系客服)
    }
}

3.5 运行结果

复制代码
========== 状态模式示例:订单管理系统 ==========

【场景1】正常购物流程

┌────────────── 订单信息 ──────────────┐
│ 订单号:ORD001
│ 客户:张三
│ 金额:¥299.00
│ 状态:待支付
└────────────────────────────────────┘

▶ 执行支付操作...
✓ 支付成功,订单状态从待支付变为待发货
┌─────────────────────────────────────────┐
│ 订单[ORD001]状态更新:待发货
└─────────────────────────────────────────┘
▶ 执行发货操作...
✓ 发货成功,订单状态从待发货变为待收货
┌─────────────────────────────────────────┐
│ 订单[ORD001]状态更新:待收货
└─────────────────────────────────────────┘
▶ 执行确认收货操作...
✓ 确认收货成功,订单完成
┌─────────────────────────────────────────┐
│ 订单[ORD001]状态更新:已完成
└─────────────────────────────────────────┘

四、Spring框架中的状态模式应用

4.1 Spring StateMachine

Spring StateMachine 是 Spring 框架对状态模式的完整实现,提供了强大的状态机功能。

java 复制代码
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
    
    @Override
    public void configure(StateMachineStateConfigurer<String, String> states) 
            throws Exception {
        states
            .withStates()
                .initial("PENDING_PAYMENT")
                .states(EnumSet.allOf(OrderStates.class));
    }
    
    @Override
    public void configure(StateMachineTransitionConfigurer<String, String> transitions) 
            throws Exception {
        transitions
            .withExternal()
                .source("PENDING_PAYMENT").target("PENDING_SHIPMENT")
                .event("PAY")
                .action(payAction())
                .and()
            .withExternal()
                .source("PENDING_SHIPMENT").target("PENDING_RECEIPT")
                .event("SHIP")
                .action(shipAction())
                .and()
            .withExternal()
                .source("PENDING_RECEIPT").target("COMPLETED")
                .event("CONFIRM")
                .action(confirmAction());
    }
    
    @Bean
    public Action<String, String> payAction() {
        return context -> {
            System.out.println("执行支付操作...");
            // 业务逻辑:扣减库存、记录日志等
            context.getExtendedState().getVariables().put("paymentTime", LocalDateTime.now());
        };
    }
    
    @Bean
    public Action<String, String> shipAction() {
        return context -> {
            System.out.println("执行发货操作...");
            // 业务逻辑:生成物流单号等
        };
    }
    
    @Bean
    public Action<String, String> confirmAction() {
        return context -> {
            System.out.println("执行确认收货操作...");
            // 业务逻辑:完成订单、评价等
        };
    }
}

/**
 * 订单状态枚举
 */
public enum OrderStates {
    PENDING_PAYMENT,    // 待支付
    PENDING_SHIPMENT,   // 待发货
    PENDING_RECEIPT,    // 待收货
    COMPLETED,          // 已完成
    CANCELLED           // 已取消
}

/**
 * 订单事件枚举
 */
public enum OrderEvents {
    PAY,    // 支付
    SHIP,   // 发货
    CONFIRM,// 确认收货
    CANCEL  // 取消
}

4.2 状态机服务层

java 复制代码
@Service
public class OrderStateMachineService {
    
    @Autowired
    private StateMachineFactory<String, String> stateMachineFactory;
    
    @Autowired
    private OrderRepository orderRepository;
    
    /**
     * 创建订单状态机
     */
    public StateMachine<String, String> createOrderStateMachine(String orderId) {
        StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine(orderId);
        stateMachine.start();
        
        // 设置订单ID到扩展状态中
        stateMachine.getExtendedState().getVariables().put("orderId", orderId);
        
        return stateMachine;
    }
    
    /**
     * 发送事件
     */
    public boolean sendEvent(String orderId, String event) {
        StateMachine<String, String> stateMachine = createOrderStateMachine(orderId);
        return stateMachine.sendEvent(event);
    }
    
    /**
     * 获取当前状态
     */
    public String getCurrentState(String orderId) {
        StateMachine<String, String> stateMachine = createOrderStateMachine(orderId);
        return stateMachine.getState().getId();
    }
    
    /**
     * 持久化状态
     */
    @Transactional
    public void persistState(String orderId, StateMachine<String, String> stateMachine) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.setStatus(stateMachine.getState().getId());
        orderRepository.save(order);
    }
}

4.3 Spring Web Flow 中的状态管理

xml 复制代码
<!-- order-flow.xml -->
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
    
    <!-- 初始状态:填写订单信息 -->
    <view-state id="enterOrderInfo" view="orderForm">
        <on-entry>
            <evaluate expression="orderController.initOrder()" result="flowScope.order"/>
        </on-entry>
        <transition on="submit" to="validateOrder"/>
        <transition on="cancel" to="cancelOrder"/>
    </view-state>
    
    <!-- 验证状态 -->
    <action-state id="validateOrder">
        <evaluate expression="orderValidator.validate(order)"/>
        <transition on="success" to="payment"/>
        <transition on="error" to="enterOrderInfo"/>
    </action-state>
    
    <!-- 支付状态 -->
    <subflow-state id="payment" subflow="payment-flow">
        <input name="order" value="order"/>
        <transition on="paymentSuccess" to="confirmOrder"/>
        <transition on="paymentFailed" to="paymentFailed"/>
    </subflow-state>
    
    <!-- 确认状态 -->
    <view-state id="confirmOrder" view="confirmOrder">
        <transition on="confirm" to="completeOrder"/>
        <transition on="back" to="enterOrderInfo"/>
    </view-state>
    
    <!-- 结束状态 -->
    <end-state id="completeOrder" view="orderComplete"/>
    <end-state id="cancelOrder" view="orderCancelled"/>
    <end-state id="paymentFailed" view="paymentFailed"/>
    
</flow>

4.4 Spring @Profile 注解的状态模式应用

java 复制代码
/**
 * 环境服务接口
 */
public interface EnvironmentService {
    String getEnvironment();
    void configure();
}

/**
 * 开发环境
 */
@Component
@Profile("dev")
@ConditionalOnProperty(name = "app.env", havingValue = "development")
public class DevEnvironmentService implements EnvironmentService {
    
    @Value("${app.debug:true}")
    private boolean debug;
    
    @Override
    public String getEnvironment() {
        return "Development Environment";
    }
    
    @Override
    public void configure() {
        System.out.println("配置开发环境...");
        if (debug) {
            System.out.println("调试模式已开启");
        }
    }
}

/**
 * 生产环境
 */
@Component
@Profile("prod")
@ConditionalOnProperty(name = "app.env", havingValue = "production")
public class ProdEnvironmentService implements EnvironmentService {
    
    @Value("${app.performance.monitoring:true}")
    private boolean performanceMonitoring;
    
    @Override
    public String getEnvironment() {
        return "Production Environment";
    }
    
    @Override
    public void configure() {
        System.out.println("配置生产环境...");
        if (performanceMonitoring) {
            System.out.println("性能监控已开启");
        }
    }
}

/**
 * 测试环境
 */
@Component
@Profile("test")
public class TestEnvironmentService implements EnvironmentService {
    
    @Override
    public String getEnvironment() {
        return "Test Environment";
    }
    
    @Override
    public void configure() {
        System.out.println("配置测试环境...");
        System.out.println("测试数据准备中...");
    }
}

五、状态模式的应用场景

5.1 典型应用场景

  1. 订单/工单系统

    • 订单状态流转(待支付 → 待发货 → 待收货 → 已完成)
    • 审批流程(待审批 → 审批中 → 已通过/已拒绝)
    • 任务状态管理
  2. 工作流引擎

    • BPMN流程管理
    • 文档审批流程
    • 请假申请流程
  3. 游戏开发

    • 角色状态(站立、奔跑、跳跃、攻击)
    • 游戏关卡状态
    • AI行为状态
  4. 多媒体应用

    • 播放器状态(播放、暂停、停止、快进)
    • 录制状态
    • 缓冲状态
  5. 网络连接

    • TCP连接状态
    • HTTP会话状态
    • WebSocket连接状态

5.2 实际业务场景代码示例

java 复制代码
/**
 * 文档审批状态示例
 */
public interface DocumentState {
    void submit(DocumentContext document);
    void approve(DocumentContext document);
    void reject(DocumentContext document);
    void archive(DocumentContext document);
}

/**
 * 草稿状态
 */
public class DraftState implements DocumentState {
    @Override
    public void submit(DocumentContext document) {
        System.out.println("文档提交审批");
        document.setState(new PendingApprovalState());
    }
    
    @Override
    public void approve(DocumentContext document) {
        System.out.println("草稿状态不能审批");
    }
    
    @Override
    public void reject(DocumentContext document) {
        System.out.println("草稿状态不能驳回");
    }
    
    @Override
    public void archive(DocumentContext document) {
        System.out.println("草稿状态不能归档");
    }
}

/**
 * 待审批状态
 */
public class PendingApprovalState implements DocumentState {
    @Override
    public void submit(DocumentContext document) {
        System.out.println("文档已提交,不能重复提交");
    }
    
    @Override
    public void approve(DocumentContext document) {
        System.out.println("文档审批通过");
        document.setState(new ApprovedState());
    }
    
    @Override
    public void reject(DocumentContext document) {
        System.out.println("文档审批驳回");
        document.setState(new RejectedState());
    }
    
    @Override
    public void archive(DocumentContext document) {
        System.out.println("待审批状态不能归档");
    }
}

六、状态模式 vs 策略模式

维度 状态模式 策略模式
目的 管理对象状态及其行为变化 封装可互换的算法
状态管理 内部维护状态,可自动转换 客户端指定策略,通常不改变
行为依赖 行为依赖于当前状态 行为由选择的策略决定
转换规则 包含状态转换逻辑 不涉及转换
适用场景 对象有多个状态,行为随状态变化 需要多种算法实现,可灵活切换

七、优缺点分析

优点

  1. 单一职责原则:将与特定状态相关的代码组织在单独的类中
  2. 开闭原则:添加新状态无需修改现有状态类
  3. 消除复杂条件语句:减少 if-else 或 switch-case 的使用
  4. 状态转换明确:状态间的转换逻辑集中管理,易于理解
  5. 提高内聚性:状态相关的行为都封装在状态类中

缺点

  1. 类数量增加:每个状态都需要一个具体类,增加系统复杂度
  2. 结构复杂化:引入更多类和接口,增加了系统抽象层次
  3. 学习成本:理解状态转换需要了解所有状态类
  4. 性能考虑:频繁的状态转换可能带来一定的性能开销

八、最佳实践建议

8.1 设计建议

  1. 使用枚举定义状态
java 复制代码
public enum OrderStatus {
    PENDING_PAYMENT("待支付"),
    PENDING_SHIPMENT("待发货"),
    PENDING_RECEIPT("待收货"),
    COMPLETED("已完成"),
    CANCELLED("已取消");
    
    private String description;
    
    OrderStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}
  1. 状态转换表配置
java 复制代码
@Component
public class StateTransitionConfig {
    
    private Map<OrderStatus, List<OrderStatus>> transitions;
    
    @PostConstruct
    public void init() {
        transitions = new HashMap<>();
        transitions.put(OrderStatus.PENDING_PAYMENT, 
            Arrays.asList(OrderStatus.PENDING_SHIPMENT, OrderStatus.CANCELLED));
        transitions.put(OrderStatus.PENDING_SHIPMENT, 
            Arrays.asList(OrderStatus.PENDING_RECEIPT, OrderStatus.CANCELLED));
        transitions.put(OrderStatus.PENDING_RECEIPT, 
            Arrays.asList(OrderStatus.COMPLETED, OrderStatus.CANCELLED));
    }
    
    public boolean isValidTransition(OrderStatus from, OrderStatus to) {
        return transitions.getOrDefault(from, Collections.emptyList()).contains(to);
    }
}
  1. 状态持久化处理
java 复制代码
@Entity
@Table(name = "orders")
public class Order {
    
    @Id
    private String orderId;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    @Version
    private Long version;  // 乐观锁,防止并发修改
    
    @Lob
    @Convert(converter = StateVariablesConverter.class)
    private Map<String, Object> stateVariables;
}

8.2 注意事项

  1. 线程安全:状态对象通常是单例的,确保无状态
  2. 并发控制:处理并发状态转换时的数据一致性问题
  3. 状态历史:记录状态变更历史便于追踪和审计
  4. 异常处理:定义清晰的状态转换异常体系

九、总结

状态模式是一种强大的行为型设计模式,它通过将状态相关的行为封装到独立的状态类中,使得对象能够根据内部状态的改变而改变其行为。

核心价值

  • 提供了一种优雅的方式来处理对象状态的复杂性
  • 将状态转换和状态行为的逻辑分离,提高代码的可维护性
  • 符合面向对象设计的开闭原则和单一职责原则

适用场景判断

当你的对象有以下特征时,考虑使用状态模式:

  • 行为依赖于其状态,并且在运行时根据状态改变行为
  • 操作中包含大量的与对象状态相关的条件语句
  • 状态转换规则复杂且可能变化
  • 需要在不同状态下共享相同的行为
相关推荐
驴儿响叮当20101 小时前
设计模式之备忘录模式
设计模式·备忘录模式
驴儿响叮当20101 小时前
设计模式之迭代器模式
设计模式·迭代器模式
qq_401700411 小时前
嵌入式C语言设计模式
c语言·开发语言·设计模式
minh_coo1 小时前
Spring单元测试之反射利器:ReflectionTestUtils
java·后端·spring·单元测试·intellij-idea
SuperEugene1 小时前
常见设计模式在 JS 里的轻量用法:单例、发布订阅、策略
前端·javascript·设计模式·面试
小米4961 小时前
Js设计模式---策略模式
设计模式·策略模式
野生技术架构师2 小时前
Spring Boot + JPackage:构建独立安装包!
java·spring boot·后端
童话的守望者2 小时前
dc9靶场通关
java·开发语言
弹简特2 小时前
【JavaEE11-后端部分】SpringMVC06-综合案例2-从用户登录看前后端交互:接口文档与HTTP通信详解
java·spring boot·spring·http·java-ee·tomcat