23种设计模式一状态模式

前言

在软件开发中,我们经常遇到这样的场景:一个对象的行为取决于其当前的状态,并且随着状态的改变,对象的行为也会发生相应的变化。比如订单从"待支付"到"已支付"再到"已发货",每种状态下能够执行的操作都不相同。

如果使用传统的if-elseswitch-case来实现这种状态相关的逻辑,代码会变得冗长、难以维护,而且每当需要新增状态或修改状态转换规则时,都要修改大量代码。**状态模式(State Pattern)**正是为了解决这类问题而诞生的。

一、什么是状态模式

1.1 定义

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

1.2 解决的核心问题

状态模式主要解决的是当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,如何将复杂的条件判断逻辑转化为清晰的状态管理。

1.3 实际应用场景

  • 订单管理系统:订单状态的流转(待支付→已支付→已发货→已完成)
  • 工作流引擎:文档审批流程(草稿→待审核→已审核→已发布)
  • 游戏开发:角色状态(站立→行走→奔跑→跳跃)
  • 线程管理:线程的生命周期(新建→就绪→运行→阻塞→终止)
  • 设备控制:设备的开关机状态、工作模式等

二、状态模式的核心结构

状态模式包含四个核心角色,每个角色都有明确的职责:

2.1 角色划分

角色 职责描述
环境类(Context) 定义客户感兴趣的接口,维护一个具体状态类的实例,将与状态相关的操作委托给当前状态对象处理
抽象状态类(State) 定义所有具体状态的共同接口,声明状态处理方法
具体状态类(ConcreteState) 实现抽象状态类定义的接口,处理环境类在该状态下的具体行为
客户端(Client) 使用环境类,通过环境类来操作状态

2.2 类图结构

复制代码
┌─────────────────┐
│    Context      │
├─────────────────┤
│ - state: State  │
├─────────────────┤
│ + setState()    │
│ + request()     │
└────────┬────────┘
         │
         │ depends on
         │
┌────────┴────────┐
│   <<interface>> │
│     State       │
├─────────────────┤
│ + handle()      │
└────────┬────────┘
         │
         │ extends
         │
    ┌────┴────┬──────────┐
    ▼         ▼          ▼
┌──────┐ ┌──────┐ ┌──────┐
│StateA│ │StateB│ │StateC│
└──────┘ └──────┘ └──────┘

三、实现步骤与代码示例

下面以订单状态流转为例,实现一个完整的状态模式示例。

3.1 场景说明

电商订单有以下状态流转:

  • 待支付(PENDING):可以取消订单、支付
  • 已支付(PAID):可以发货、退款
  • 已发货(SHIPPED):可以确认收货、申请退货
  • 已完成(COMPLETED):订单结束,可评价
  • 已取消(CANCELLED):订单结束,无后续操作

3.2 代码实现

3.2.1 抽象状态类

java 复制代码
/**
 * 订单状态抽象类
 * 定义所有状态共有的接口
 */
public abstract class OrderState {
    
    protected OrderContext orderContext;
    
    public OrderState(OrderContext orderContext) {
        this.orderContext = orderContext;
    }
    
    // 支付操作
    public abstract void pay();
    
    // 发货操作
    public abstract void ship();
    
    // 确认收货操作
    public abstract void confirmReceipt();
    
    // 取消订单操作
    public abstract void cancel();
    
    // 获取当前状态名称
    public abstract String getStateName();
}

3.2.2 环境类(Context)

java 复制代码
/**
 * 订单环境类
 * 维护订单状态,对外提供操作接口
 */
public class OrderContext {
    private String orderId;
    private OrderState currentState;
    private double amount;
    
    public OrderContext(String orderId, double amount) {
        this.orderId = orderId;
        this.amount = amount;
        // 初始状态为待支付
        this.currentState = new PendingState(this);
    }
    
    // 设置状态
    public void setState(OrderState state) {
        this.currentState = state;
        System.out.println("订单 [" + orderId + "] 状态变更为: " + state.getStateName());
    }
    
    // 委托给具体状态类处理
    public void pay() {
        currentState.pay();
    }
    
    public void ship() {
        currentState.ship();
    }
    
    public void confirmReceipt() {
        currentState.confirmReceipt();
    }
    
    public void cancel() {
        currentState.cancel();
    }
    
    // Getter方法
    public String getOrderId() {
        return orderId;
    }
    
    public double getAmount() {
        return amount;
    }
}

3.2.3 具体状态类

待支付状态:

java 复制代码
/**
 * 待支付状态
 * 可以支付或取消订单
 */
public class PendingState extends OrderState {
    
    public PendingState(OrderContext orderContext) {
        super(orderContext);
    }
    
    @Override
    public void pay() {
        System.out.println("正在支付订单,金额: " + orderContext.getAmount() + "元");
        // 状态流转:待支付 → 已支付
        orderContext.setState(new PaidState(orderContext));
        System.out.println("支付成功!");
    }
    
    @Override
    public void ship() {
        System.out.println("错误:待支付状态下不能发货!");
    }
    
    @Override
    public void confirmReceipt() {
        System.out.println("错误:待支付状态下不能确认收货!");
    }
    
    @Override
    public void cancel() {
        System.out.println("取消订单成功");
        // 状态流转:待支付 → 已取消
        orderContext.setState(new CancelledState(orderContext));
    }
    
    @Override
    public String getStateName() {
        return "待支付";
    }
}

已支付状态:

java 复制代码
/**
 * 已支付状态
 * 可以发货
 */
public class PaidState extends OrderState {
    
    public PaidState(OrderContext orderContext) {
        super(orderContext);
    }
    
    @Override
    public void pay() {
        System.out.println("错误:已支付状态下不能重复支付!");
    }
    
    @Override
    public void ship() {
        System.out.println("正在为您发货...");
        // 状态流转:已支付 → 已发货
        orderContext.setState(new ShippedState(orderContext));
        System.out.println("发货成功!");
    }
    
    @Override
    public void confirmReceipt() {
        System.out.println("错误:已支付状态下不能确认收货!");
    }
    
    @Override
    public void cancel() {
        System.out.println("错误:已支付状态下不能直接取消,请先申请退款!");
    }
    
    @Override
    public String getStateName() {
        return "已支付";
    }
}

已发货状态:

java 复制代码
/**
 * 已发货状态
 * 可以确认收货
 */
public class ShippedState extends OrderState {
    
    public ShippedState(OrderContext orderContext) {
        super(orderContext);
    }
    
    @Override
    public void pay() {
        System.out.println("错误:已发货状态下不能支付!");
    }
    
    @Override
    public void ship() {
        System.out.println("错误:已发货状态下不能重复发货!");
    }
    
    @Override
    public void confirmReceipt() {
        System.out.println("感谢您确认收货!");
        // 状态流转:已发货 → 已完成
        orderContext.setState(new CompletedState(orderContext));
        System.out.println("订单已完成");
    }
    
    @Override
    public void cancel() {
        System.out.println("错误:已发货状态下不能取消,请先申请退货!");
    }
    
    @Override
    public String getStateName() {
        return "已发货";
    }
}

已完成状态:

java 复制代码
/**
 * 已完成状态
 * 订单结束状态
 */
public class CompletedState extends OrderState {
    
    public CompletedState(OrderContext orderContext) {
        super(orderContext);
    }
    
    @Override
    public void pay() {
        System.out.println("错误:订单已完成,无法支付!");
    }
    
    @Override
    public void ship() {
        System.out.println("错误:订单已完成,无法发货!");
    }
    
    @Override
    public void confirmReceipt() {
        System.out.println("错误:订单已完成,不能重复确认收货!");
    }
    
    @Override
    public void cancel() {
        System.out.println("错误:订单已完成,无法取消!");
    }
    
    @Override
    public String getStateName() {
        return "已完成";
    }
}

已取消状态:

java 复制代码
/**
 * 已取消状态
 * 订单结束状态
 */
public class CancelledState extends OrderState {
    
    public CancelledState(OrderContext orderContext) {
        super(orderContext);
    }
    
    @Override
    public void pay() {
        System.out.println("错误:订单已取消,无法支付!");
    }
    
    @Override
    public void ship() {
        System.out.println("错误:订单已取消,无法发货!");
    }
    
    @Override
    public void confirmReceipt() {
        System.out.println("错误:订单已取消,无法确认收货!");
    }
    
    @Override
    public void cancel() {
        System.out.println("错误:订单已取消,不能重复取消!");
    }
    
    @Override
    public String getStateName() {
        return "已取消";
    }
}

3.2.4 客户端测试

java 复制代码
/**
 * 状态模式测试客户端
 */
public class StatePatternDemo {
    public static void main(String[] args) {
        System.out.println("========== 订单状态流转演示 ==========\n");
        
        // 创建订单
        OrderContext order = new OrderContext("ORD20240112001", 299.00);
        System.out.println();
        
        // 测试1:正常流程
        System.out.println("--- 测试正常流程 ---");
        order.pay();        // 待支付 → 已支付
        order.ship();       // 已支付 → 已发货
        order.confirmReceipt(); // 已发货 → 已完成
        
        System.out.println("\n--- 创建新订单测试异常操作 ---");
        
        // 测试2:取消流程
        OrderContext order2 = new OrderContext("ORD20240112002", 158.00);
        order2.cancel();    // 待支付 → 已取消
        order2.pay();       // 已取消状态下尝试支付(应该失败)
        
        System.out.println("\n--- 测试状态非法操作 ---");
        
        // 测试3:已支付状态下尝试取消
        OrderContext order3 = new OrderContext("ORD20240112003", 599.00);
        order3.pay();
        order3.cancel();    // 已支付状态下取消(应该失败)
    }
}

输出结果:

复制代码
========== 订单状态流转演示 ==========

订单 [ORD20240112001] 状态变更为: 待支付

--- 测试正常流程 ---
正在支付订单,金额: 299.0元
订单 [ORD20240112001] 状态变更为: 已支付
支付成功!
正在为您发货...
订单 [ORD20240112001] 状态变更为: 已发货
发货成功!
感谢您确认收货!
订单 [ORD20240112001] 状态变更为: 已完成
订单已完成

--- 创建新订单测试异常操作 ---
订单 [ORD20240112002] 状态变更为: 待支付
取消订单成功
订单 [ORD20240112002] 状态变更为: 已取消
错误:订单已取消,无法支付!

--- 测试状态非法操作 ---
订单 [ORD20240112003] 状态变更为: 待支付
正在支付订单,金额: 599.0元
订单 [ORD20240112003] 状态变更为: 已支付
支付成功!
错误:已支付状态下不能直接取消,请先申请退款!

四、状态模式的优缺点分析

4.1 优势

优势 说明
逻辑分离 将不同状态的行为分散到不同的类中,消除了大量的条件判断语句,代码结构清晰
扩展性强 新增状态只需增加新的状态类,无需修改现有代码,符合开闭原则
状态转换集中 状态转换逻辑由状态类自己管理,避免了状态转换分散在各个方法中
易于维护 每个状态类的职责单一,修改某个状态的行为不会影响其他状态
符合单一职责 每个具体状态类只负责一种状态下的行为处理

4.2 潜在不足

缺点 说明
类数量增加 每个状态都需要一个独立的类,当状态数量较多时,类的数量会显著增加
状态间通信 某些状态可能需要访问其他状态的信息,状态之间的耦合度可能增加
过度设计风险 对于简单的状态逻辑,使用状态模式可能显得过于复杂
学习成本 相比传统的if-else实现,状态模式的理解和实现成本稍高

五、应用场景总结

5.1 适合使用状态模式的场景

  1. 订单管理系统

    • 电商订单状态流转、退款申请处理、售后状态管理
  2. 工作流审批系统

    • 文档审批流程、请假审批、报销审批等多级审批场景
  3. 游戏角色状态管理

    • 角色的各种状态(站立、行走、奔跑、跳跃、攻击、受伤等)
    • 技能冷却、增益/减益效果的状态管理
  4. 设备控制系统

    • IoT设备的工作模式切换(待机、运行、维护、故障等)
    • 自动售货机的状态管理(空闲、投币、出货、缺货等)
  5. 交通信号灯系统

    • 红绿灯的状态切换(红灯→绿灯→黄灯→红灯)
    • 铁路道口的状态管理

5.2 不适合使用状态模式的场景

  • 状态非常简单(只有2-3个状态),且状态转换逻辑简单
  • 状态很少变化,未来不太可能新增状态
  • 一次性代码或临时性项目

六、与其他设计模式的对比

6.1 状态模式 vs 策略模式

对比维度 状态模式 策略模式
目的 管理对象的状态转换 定义算法族, interchangeable
状态转换 状态类自己负责状态转换 客户端决定使用哪个策略
行为依赖 行为随内部状态改变 算法可独立变化
调用关系 状态类持有Context引用 策略类不持有Context引用
典型应用 订单流转、工作流 排序算法、支付方式

核心区别 :状态模式的状态是自动流转 的,由状态类决定下一个状态;而策略模式的策略选择是手动的,由客户端决定使用哪个策略。

6.2 状态模式 vs 观察者模式

对比维度 状态模式 观察者模式
关注点 对象内部状态和行为的变化 多个对象对主题变化的响应
解耦方向 解耦状态和行为 解耦事件发布和订阅
使用场景 单对象的状态管理 一对多的消息通知

6.3 状态模式 vs 责任链模式

两者都可以处理流程式的操作,但本质不同:

  • 状态模式:每个状态处理该状态下的行为,状态之间有明确的转换关系
  • 责任链模式:处理者按链式顺序处理请求,直到有处理者处理成功

七、最佳实践与注意事项

7.1 最佳实践

1. 合理设计状态粒度

java 复制代码
// ❌ 过于细粒度:将"已支付待发货"拆分为多个状态
public class PaidWaitShippingState extends OrderState { }
public class PaidShippingPreparingState extends OrderState { }

// ✅ 合理粒度:合并为一个"已支付"状态
public class PaidState extends OrderState {
    @Override
    public void ship() {
        // 在内部处理发货准备工作
        System.out.println("准备发货...");
        orderContext.setState(new ShippedState(orderContext));
    }
}

2. 使用枚举管理状态常量

java 复制代码
/**
 * 订单状态枚举
 */
public enum OrderStatus {
    PENDING("待支付"),
    PAID("已支付"),
    SHIPPED("已发货"),
    COMPLETED("已完成"),
    CANCELLED("已取消");
    
    private String displayName;
    
    OrderStatus(String displayName) {
        this.displayName = displayName;
    }
    
    public String getDisplayName() {
        return displayName;
    }
}

3. 状态持久化

在数据库中记录订单状态时,建议使用状态枚举的name()或ordinal()值:

java 复制代码
public class Order {
    private String orderId;
    private String status;  // 存储状态名称,如 "PAID"
    // ...
}

4. 状态机可视化

对于复杂的状态转换,建议使用状态机图进行可视化设计:
支付
取消
发货
确认收货
PENDING
PAID
CANCELLED
SHIPPED
COMPLETED

7.2 避坑指南

1. 避免状态循环引用

java 复制代码
// ❌ 危险:状态之间直接相互持有引用
public class StateA extends OrderState {
    private StateB stateB;  // 避免这样
}

// ✅ 正确:通过Context管理状态
public class StateA extends OrderState {
    @Override
    public void handle() {
        // 通过Context切换到下一个状态
        orderContext.setState(new StateB(orderContext));
    }
}

2. 考虑状态转换的原子性

java 复制代码
@Override
public void pay() {
    // ❌ 不安全:状态切换过程中可能被中断
    orderContext.setState(new PaidState(orderContext));
    updateDatabase();  // 如果这里失败,状态已切换但数据库未更新
    
    // ✅ 安全:先处理业务逻辑,再切换状态
    if (updateDatabase()) {
        orderContext.setState(new PaidState(orderContext));
    }
}

3. 状态转换的合法性检查

java 复制代码
@Override
public void pay() {
    // 检查状态转换是否合法
    if (!canTransitionTo(PaidState.class)) {
        throw new IllegalStateException("当前状态不允许支付");
    }
    
    // 执行业务逻辑
    processPayment();
    
    // 切换状态
    orderContext.setState(new PaidState(orderContext));
}

4. 状态历史的记录

在某些业务场景下,需要记录状态变化历史:

java 复制代码
public class OrderContext {
    private List<StateHistory> stateHistory = new ArrayList<>();
    
    public void setState(OrderState state) {
        // 记录状态历史
        stateHistory.add(new StateHistory(
            LocalDateTime.now(),
            this.currentState.getStateName(),
            state.getStateName()
        ));
        
        this.currentState = state;
    }
    
    // 状态历史记录类
    private static class StateHistory {
        private LocalDateTime changeTime;
        private String fromState;
        private String toState;
        // getter/setter...
    }
}

5. 结合Spring框架使用

在Spring项目中,可以将状态类注册为Bean:

java 复制代码
@Component
public class PaidState extends OrderState {
    
    @Autowired
    private PaymentService paymentService;
    
    @Override
    public void pay() {
        paymentService.processPayment(orderContext.getOrderId());
        orderContext.setState(new ShippedState(orderContext));
    }
}

7.3 进阶优化

1. 使用状态机框架

对于复杂的状态管理,可以考虑使用Spring State Machine、Squirrel State Machine等专业框架:

java 复制代码
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
    
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
        states
            .withStates()
            .initial(OrderStatus.PENDING)
            .states(EnumSet.allOf(OrderStatus.class));
    }
    
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
        transitions
            .withExternal()
            .source(OrderStatus.PENDING).target(OrderStatus.PAID).event(OrderEvent.PAY)
            .and()
            .withExternal()
            .source(OrderStatus.PAID).target(OrderStatus.SHIPPED).event(OrderEvent.SHIP);
    }
}

2. 状态模式与策略模式结合

某些场景下,可以将状态模式和策略模式结合使用,在一个状态下使用不同的策略:

java 复制代码
public class PaidState extends OrderState {
    
    private ShippingStrategy shippingStrategy;
    
    public PaidState(OrderContext orderContext, ShippingStrategy shippingStrategy) {
        super(orderContext);
        this.shippingStrategy = shippingStrategy;
    }
    
    @Override
    public void ship() {
        // 使用策略模式处理不同发货方式
        shippingStrategy.ship(orderContext);
        orderContext.setState(new ShippedState(orderContext));
    }
}

八、总结

状态模式是一种非常实用的设计模式,特别适合处理对象行为随状态变化的复杂业务场景。通过将状态逻辑分散到独立的状态类中,状态模式让代码结构更加清晰,维护成本显著降低。

核心要点回顾:

  1. 消除复杂的条件判断:用状态类替换if-else
  2. 符合开闭原则:新增状态无需修改现有代码
  3. 状态转换集中管理:每个状态类负责自己的转换逻辑
  4. 职责单一:每个状态类只关注一种状态下的行为

使用建议:

  • 当状态数量 ≥ 3,且状态转换逻辑复杂时,优先考虑状态模式
  • 简单场景(2-3个状态)使用枚举+简单判断即可
  • 结合状态机框架处理超复杂的状态管理
  • 注意状态持久化和状态历史的记录

掌握状态模式,能让你在面对复杂的状态管理需求时游刃有余,写出更加优雅、可维护的代码!

相关推荐
木斯佳4 小时前
前端八股文面经大全:有赞前端一面二面HR面(2026-1-13)·面经深度解析
前端·状态模式
Coder_Boy_8 小时前
Java高级_资深_架构岗 核心知识点全解析(模块四:分布式)
java·spring boot·分布式·微服务·设计模式·架构
前端不太难10 小时前
未来的鸿蒙 App,还需要“首页”吗?
华为·状态模式·harmonyos
资深web全栈开发21 小时前
设计模式之解释器模式 (Interpreter Pattern)
设计模式·解释器模式
漠月瑾-西安21 小时前
React-Redux Connect 高阶组件:从“桥梁”到“智能管家”的深度解析
react.js·设计模式·react-redux·高阶组件·connect高阶租单间·原理理解
J_liaty1 天前
23种设计模式一备忘录模式
设计模式·备忘录模式
木斯佳1 天前
前端八股文面经大全:2026-01-23快手AI应用方向前端实习一面面经深度解析
前端·人工智能·状态模式
驴儿响叮当20101 天前
设计模式之建造者模式
设计模式·建造者模式
知识即是力量ol1 天前
口语八股—— Spring 面试实战指南(终篇):常用注解篇、Spring中的设计模式
java·spring·设计模式·面试·八股·常用注解