深入理解设计模式:状态模式(State Pattern)

在软件开发中,我们经常会遇到对象的行为随着其内部状态的变化而变化的情况。例如,一个订单可能处于"待支付"、"已支付"、"已发货"或"已完成"等不同状态,每个状态下订单的操作逻辑可能完全不同。如果直接在代码中使用大量的if-elseswitch-case语句来判断状态,会导致代码臃肿、难以维护,并且违反开闭原则(OCP)

状态模式(State Pattern)提供了一种优雅的解决方案,它允许对象在运行时根据内部状态改变其行为,而不必在代码中显式地检查状态。本文将深入探讨状态模式的核心概念、实现方式、优缺点以及实际应用场景,并通过代码示例帮助读者更好地理解该模式。

1. 什么是状态模式?

1.1 定义

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

1.2 核心思想

  • 将状态抽象成独立的类,每个状态类负责处理该状态下的行为。

  • 上下文(Context)对象持有一个状态对象的引用,并将行为委托给当前状态对象。

  • 状态转换由状态类自身或上下文类控制,而不是在业务逻辑中硬编码。

1.3 适用场景

  • 对象的行为依赖于它的状态,并且需要在运行时动态改变行为。

  • 代码中包含大量与对象状态相关的条件分支语句,导致逻辑复杂。

  • 状态转换逻辑较复杂,且未来可能新增状态。

2. 状态模式的结构

状态模式主要由以下几个角色组成:

角色 作用
Context(上下文) 定义客户端接口,维护当前状态对象。
State(抽象状态) 定义状态接口,封装与特定状态相关的行为。
ConcreteState(具体状态) 实现状态接口,处理该状态下的行为逻辑,并可触发状态转换。

2.1 UML 类图

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

3. 代码示例:订单状态管理

假设我们有一个订单系统,订单可能处于以下状态:

  • 待支付(Pending)

  • 已支付(Paid)

  • 已发货(Shipped)

  • 已完成(Completed)

3.1 定义状态接口

复制代码
public interface OrderState {
    void next(Order order);
    void prev(Order order);
    void printStatus();
}

3.2 实现具体状态类

复制代码
// 待支付状态
public class Pending implements OrderState {
    @Override
    public void next(Order order) {
        order.setState(new Paid());
    }

    @Override
    public void prev(Order order) {
        System.out.println("订单尚未支付,无法回退。");
    }

    @Override
    public void printStatus() {
        System.out.println("订单状态:待支付");
    }
}

// 已支付状态
public class Paid implements OrderState {
    @Override
    public void next(Order order) {
        order.setState(new Shipped());
    }

    @Override
    public void prev(Order order) {
        order.setState(new Pending());
    }

    @Override
    public void printStatus() {
        System.out.println("订单状态:已支付");
    }
}

// 已发货状态
public class Shipped implements OrderState {
    @Override
    public void next(Order order) {
        order.setState(new Completed());
    }

    @Override
    public void prev(Order order) {
        order.setState(new Paid());
    }

    @Override
    public void printStatus() {
        System.out.println("订单状态:已发货");
    }
}

// 已完成状态
public class Completed implements OrderState {
    @Override
    public void next(Order order) {
        System.out.println("订单已完成,无法继续推进。");
    }

    @Override
    public void prev(Order order) {
        order.setState(new Shipped());
    }

    @Override
    public void printStatus() {
        System.out.println("订单状态:已完成");
    }
}

3.3 定义上下文类(Order)

复制代码
public class Order {
    private OrderState state;

    public Order() {
        this.state = new Pending(); // 初始状态:待支付
    }

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

    public void nextState() {
        state.next(this);
    }

    public void prevState() {
        state.prev(this);
    }

    public void printStatus() {
        state.printStatus();
    }
}

3.4 客户端测试

复制代码
public class Client {
    public static void main(String[] args) {
        Order order = new Order();
        order.printStatus(); // 待支付

        order.nextState();   // 推进到已支付
        order.printStatus(); // 已支付

        order.nextState();   // 推进到已发货
        order.printStatus(); // 已发货

        order.prevState();   // 回退到已支付
        order.printStatus(); // 已支付

        order.nextState();   // 推进到已发货
        order.nextState();   // 推进到已完成
        order.printStatus(); // 已完成
    }
}

输出结果:

复制代码
订单状态:待支付
订单状态:已支付
订单状态:已发货
订单状态:已支付
订单状态:已发货
订单状态:已完成

4. 状态模式的优缺点

4.1 优点

符合单一职责原则 :每个状态类只负责自己的行为逻辑。

符合开闭原则(OCP) :新增状态时无需修改现有代码。

减少条件分支 :避免复杂的if-elseswitch-case逻辑。

状态转换逻辑清晰:状态转换由状态类自身控制,易于维护。

4.2 缺点

类数量增加 :每个状态都需要一个单独的类,可能导致类膨胀。

状态转换逻辑分散 :如果状态转换逻辑复杂,可能难以追踪。

可能引入循环依赖:状态类可能依赖上下文类,反之亦然。

5. 状态模式的实际应用

5.1 电商订单系统

  • 订单状态流转:待支付 → 已支付 → 已发货 → 已完成

  • 不同状态下,订单的操作(如取消、退款)逻辑不同。

5.2 游戏角色状态

  • 角色可能有站立奔跑跳跃攻击等状态。

  • 不同状态下,角色的动画、移动速度、碰撞检测等行为不同。

5.3 线程状态管理

  • Java 线程的状态:NEWRUNNABLEBLOCKEDWAITINGTERMINATED

  • 不同状态下,线程的调度策略不同。

5.4 UI 组件状态

  • 按钮的状态:正常悬停按下禁用

  • 不同状态下,按钮的样式和交互逻辑不同。

6. 状态模式 vs. 策略模式

状态模式和策略模式在结构上非常相似,但它们的设计意图不同

对比点 状态模式 策略模式
目的 让对象在不同状态下改变行为 让客户端选择不同的算法
状态转换 状态类可以自动切换 策略通常由客户端设置
依赖关系 状态类可能依赖上下文 策略类通常独立

7. 总结

状态模式是一种强大的设计模式,特别适用于对象行为随状态变化而变化的场景。它通过将状态逻辑分散到不同的类中,使得代码更加清晰、可扩展。尽管它可能增加类的数量,但在复杂状态管理场景下,它能显著提升代码的可维护性。

适用场景总结:

  • 对象有多个状态,且行为随状态变化。

  • 代码中有大量状态相关的条件分支。

  • 未来可能新增状态,需要灵活扩展。

推荐使用场景:

✅ 订单状态管理

✅ 游戏角色状态切换

✅ 线程/进程状态管理

✅ UI 组件交互状态

相关推荐
敲代码的约德尔人31 分钟前
JavaScript 设计模式完全指南
javascript·设计模式
I'm Jie3 小时前
Swagger UI 本地化部署,解决 FastAPI Swagger UI 依赖外部 CDN 加载失败问题
python·ui·fastapi·swagger·swagger ui
爱学习的程序媛4 小时前
【Web前端】优化Core Web Vitals提升用户体验
前端·ui·web·ux·用户体验
爱学习的程序媛5 小时前
【Web前端】前端用户体验优化全攻略
前端·ui·交互·web·ux·用户体验
han_5 小时前
JavaScript设计模式(二):策略模式实现与应用
前端·javascript·设计模式
紫丁香5 小时前
Selenium自动化测试详解1
python·selenium·测试工具·ui
GISer_Jing5 小时前
前端组件库——shadcn/ui:轻量、自由、可拥有,解锁前端组件库的AI时代未来
前端·人工智能·ui
阿珊和她的猫8 小时前
微信小程序静默授权异步问题的处理方案
微信小程序·状态模式·notepad++
rjc_lihui8 小时前
IntelliSense: 无法打开 源 文件 “ui_mainwindow.h“ demo\qtdemosrc\mainwindow
ui
前端不太难20 小时前
ArkUI 的页面生命周期详解
状态模式