设计模式——状态设计模式(行为型)

article/2025/8/13 15:44:48

摘要

状态设计模式是一种行为型设计模式,核心在于允许对象在内部状态改变时改变行为。它通过状态对象封装不同行为,使状态切换灵活清晰。该模式包含环境类、抽象状态类和具体状态类等角色,具有避免大量分支判断、符合单一职责和开闭原则等特点。适用于订单状态管理、流程审批等场景,其结构清晰,实现方式多样,能有效解决状态切换问题。

1. 状态设计模式定义

状态设计模式(State Pattern)是一种行为型设计模式,它的核心思想是:允许对象在其内部状态改变时改变它的行为,使得看起来就像修改了它的类。状态模式允许一个对象在其内部状态发生改变时改变它的行为。这个对象看起来就像修改了它的类一样。

状态模式就像一个“带有状态的自动售货机”——投币、选择商品、出货,每个动作在不同状态下有不同结果。我们通过状态对象来封装不同的行为,让状态切换变得灵活而清晰。

1.1. 🧩 角色组成:

角色

说明

Context

环境类,持有当前状态,定义对外接口,委托状态对象处理行为

State

抽象状态接口,定义所有状态的行为方法

ConcreteState

具体状态类,实现不同状态下的行为逻辑,并负责状态切换

1.2. ✅ 特点

  • 避免了大量 if-elseswitch-case 分支判断
  • 每个状态封装一个独立的行为逻辑,符合单一职责原则
  • 状态切换内聚在状态对象内部,符合“开闭原则”

1.3. 🧾 示例场景

  • 订单状态管理(待支付、已支付、已发货、已完成)
  • 流程审批引擎(待审核、审核中、已驳回、已通过)
  • 自动售货机、工作流、任务调度状态机等

2. 状态设计模式结构

状态模式包含如下角色:

  • Context: 环境类
  • State: 抽象状态类
  • ConcreteState: 具体状态类

2.1. 状态设计模式类图

2.2. 状态设计模式时序图

3. 状态设计模式实现方式

状态设计模式的实现方式主要依赖对象状态的封装状态间的转换控制。以下是标准实现方式及其在 Java(或类似面向对象语言)中的常见实现结构。

3.1. 定义抽象状态接口(State

public interface State {void handle(Context context);
}

3.2. 定义具体状态类(ConcreteStateA, ConcreteStateB

public class ConcreteStateA implements State {@Overridepublic void handle(Context context) {System.out.println("当前状态:A,处理逻辑中...切换到状态B");context.setState(new ConcreteStateB());}
}public class ConcreteStateB implements State {@Overridepublic void handle(Context context) {System.out.println("当前状态:B,处理逻辑中...切换到状态A");context.setState(new ConcreteStateA());}
}

3.3. 定义上下文(环境类 Context

public class Context {private State state;public Context(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void request() {state.handle(this); // 委托当前状态处理}
}

3.4. 客户端调用示例

public class Main {public static void main(String[] args) {Context context = new Context(new ConcreteStateA());context.request(); // 当前状态Acontext.request(); // 切换到Bcontext.request(); // 再切换到A}
}

3.5. 状态设计模式在Spring 或企业项目中的实现方式拓展

在实际项目中,状态模式常结合如下技术使用:

技术栈/机制

实现方式说明

Spring 容器管理状态类

使用注解 @Component注入各个状态类,用 Map<String, State>自动管理所有状态实现

状态与事件触发分离

可结合策略模式或状态机框架(如 Spring Statemachine)来支持更复杂的状态转移图

结合枚举做状态标识

使用枚举表示状态常量,再映射到状态实现类,便于状态持久化与切换

结合数据库存储状态值

在业务实体中存储当前状态字段,状态类根据字段值决定是否允许转移

3.6. Java 项目中使用状态模式的一些变体实现方式

实现方式

说明

经典接口实现

每个状态一个类,符合设计原则,但类多

枚举实现状态

enum 实现 State 接口,每个枚举项表示一个状态,简化类数量

策略+状态融合

每个状态类封装为一个策略行为,通过上下文统一调度

注解驱动(配合 AOP)

某些行为切换状态可用注解标注事件,然后由切面完成状态更新

总结:状态模式通过将“行为”委托给“状态类”,并在状态类内部控制状态转移,既解耦了状态判断逻辑,也增强了可扩展性和灵活性。

4. 状态设计模式适合场景

4.1. ✅ 适合使用状态模式的场景

场景

说明

对象状态经常变化

如订单、任务、审批流等有多个明确状态,且状态切换频繁、规则复杂。

状态行为复杂且相互不同

各状态对应的行为差异大,不适合用 if/else 处理。例如支付状态下不能发货,发货状态下不能退款。

状态切换有明确流程或图谱

如状态转移图能清晰表示状态之间的合法路径,适合模型驱动开发。

希望将状态行为局部化

避免大量 if-else/switch,在每个状态类中封装对应逻辑,提高代码清晰度。

可扩展性要求高

新增状态时只需添加一个类,符合开闭原则,不需改动原有逻辑。

工作流引擎/状态机系统开发

状态流转是核心功能,状态模式天然适配这类系统。

4.2. ❌ 不适合使用状态模式的场景

场景

原因

状态数量很少,逻辑简单

如只有 2-3 个状态、行为简单,使用状态类反而增加复杂度。

状态行为一致,仅数据不同

行为一致可以用策略模式或配置表来处理,无需状态类区分。

状态转移规则频繁变化或不稳定

状态图不稳定会导致大量状态类频繁调整,维护成本高。

只需要一个条件判断即可处理的逻辑

强行拆成多个状态类会让代码变啰嗦、不易维护。

资源受限的场景(嵌入式、移动端)

每个状态一个类可能会增加内存开销,不如用状态码枚举和 switch 实现更轻量。

4.3. 🧠 总结:

如果你面对的是有限状态机问题(FSM),状态之间行为差异大且需频繁切换,那状态设计模式就是非常合适的选择;反之,则应避免“为了模式而模式”。

5. 状态设计模式实战示例

5.1. ✅ 场景说明:

模拟一个信贷风控审批流程,审批任务的状态有以下几种:

  • 待初审初审通过复审通过审批完成
  • 各个状态下行为不同,如“提交”、“退回”、“终止”等

5.2. ✅ 类结构图(State 模式组成)

[State]            ← 抽象接口↑   ↑   ↑
[AState] [BState] [CState]  ← 具体状态类[ApprovalContext] ← 上下文类,包含状态对象 + 状态切换逻辑

5.3. 🛠 抽象状态接口

public interface ApprovalState {void submit(ApprovalContext context);void reject(ApprovalContext context);String getStateCode(); // 标识状态
}

5.4. 🛠 上下文类(使用 Spring 注解注入状态对象)

@Component
public class ApprovalContext {// 所有状态实现类注入到 Map,key 为状态 code@Autowiredprivate List<ApprovalState> stateList;private Map<String, ApprovalState> stateMap;private ApprovalState currentState;@PostConstructpublic void init() {stateMap = stateList.stream().collect(Collectors.toMap(ApprovalState::getStateCode, s -> s));}public void setCurrentState(String stateCode) {this.currentState = stateMap.get(stateCode);}public void submit() {currentState.submit(this);}public void reject() {currentState.reject(this);}public String getCurrentStateCode() {return currentState.getStateCode();}
}

5.5. 🛠 具体状态实现类(以“待初审”为例)

@Component
public class WaitInitialApprovalState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("当前状态:待初审 → 执行提交 → 切换到初审通过");context.setCurrentState("APPROVED_INIT");}@Overridepublic void reject(ApprovalContext context) {System.out.println("当前状态:待初审 → 执行驳回 → 切换到终止状态");context.setCurrentState("TERMINATED");}@Overridepublic String getStateCode() {return "WAIT_INIT";}
}

再如 “初审通过” 状态:

@Component
public class ApprovedInitialState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("当前状态:初审通过 → 执行提交 → 切换到复审通过");context.setCurrentState("APPROVED_FINAL");}@Overridepublic void reject(ApprovalContext context) {System.out.println("当前状态:初审通过 → 驳回 → 回到初审");context.setCurrentState("WAIT_INIT");}@Overridepublic String getStateCode() {return "APPROVED_INIT";}
}

5.6. 🧪 控制层模拟调用

@RestController
@RequestMapping("/approval")
public class ApprovalController {@Autowiredprivate ApprovalContext context;@GetMapping("/start")public String start() {context.setCurrentState("WAIT_INIT");return "流程已启动,当前状态:" + context.getCurrentStateCode();}@PostMapping("/submit")public String submit() {context.submit();return "提交后,当前状态:" + context.getCurrentStateCode();}@PostMapping("/reject")public String reject() {context.reject();return "驳回后,当前状态:" + context.getCurrentStateCode();}
}

5.7. 🧠 技术亮点

说明

✅ Spring 注解注入

使用 @Component + @Autowired 注解将所有状态自动注册并注入上下文

✅ Map 管理状态

使用 @PostConstruct 构建状态映射,方便通过 stateCode 切换状态

✅ 避免构造注入

通过字段注入 List<ApprovalState>,不依赖构造函数

✅ 易于扩展

添加新状态只需新增一个实现类,无需修改原有逻辑,符合开闭原则

5.8. ✅ 总结:

本示例将状态设计模式与 Spring 注解机制结合,实现了一个灵活、可扩展的金融风控审批流程状态机,适合真实风控系统中审批流、处理流的状态管理需求。

6. 状态设计模式思考

6.1. 状态设计模式与状态机设计有什么关系?

状态设计模式(State Pattern)与状态机(State Machine)有密切关系,但两者侧重点不同。理解它们的联系与区别有助于你在项目中正确选择使用方式,特别是在风控、审批流等系统中。

6.1.1. ✅ 二者关系概述:

项目

状态设计模式(State Pattern)

状态机(State Machine)

核心概念

将状态行为封装为类,行为由当前状态对象决定

明确的状态集合、事件集合、转移规则

关注点

封装状态行为和状态切换逻辑(面向对象)

管理状态 + 事件 + 转移图谱(面向模型)

表达能力

表达某对象在不同状态下行为不同

能描述复杂的状态流、事件触发与状态转移

实现方式

使用状态类、上下文对象,代码驱动

可以是代码、配置、状态转移图、框架等

适合场景

状态数有限,行为差异大,关注行为封装

状态众多,转移复杂,关注状态流和路径

是否一定使用类

✅ 每个状态类实现接口

❌ 可纯配置(如状态转移表、DSL)

常见代表

状态模式(GoF)

有限状态机(FSM)、Spring Statemachine

6.1.2. ✅ 类比举例:审批流程

  • 状态设计模式
    你创建 WaitApprovalStateApprovedStateRejectedState 等类,在类中定义“提交”、“驳回”等行为。
  • 状态机模型
    你建一个状态图:
    WAIT_APPROVAL --(submit)--> APPROVED
    WAIT_APPROVAL --(reject)--> REJECTED
    并用框架(如 Spring Statemachine)实现。

6.1.3. ✅ 状态设计模式 vs 状态机的总结图解:

                    ┌─────────────────────┐│   状态设计模式       ││  封装状态行为         ││  各状态一个类         │└────────┬────────────┘│▼状态逻辑复杂,可扩展性强│▼┌─────────────────────┐│    状态机模型        ││  管理状态转移路径     ││  可视化或配置驱动     │└─────────────────────┘

6.2. ✅ 实践建议

情况

推荐方案

状态较少、行为差异大

使用状态设计模式,可读性强、便于扩展

状态较多、转移复杂、事件驱动

使用状态机模型(框架),如 Spring Statemachine

想表达清晰状态流

先画出状态图,用状态机模型来支撑业务流程设计

6.3. 🧠 总结:

状态设计模式是状态机的一种实现方式,适用于行为封装;而状态机更偏向建模工具,适用于流程表达和自动化管理。两者可以结合使用,如状态类 + 状态图配置来实现灵活状态系统。

博文参考

  • 4. 状态模式 — Graphic Design Patterns
  • 状态设计模式
  • 设计模式之状态模式 | DESIGN

http://www.hkcw.cn/article/LeFBEJqjsG.shtml

相关文章

搜索引擎2.0(based elasticsearch6.8)设计与实现细节(完整版)

1 简介 1.1 背景 《搜索引擎onesearch 1.0-设计与实现.docx》介绍了1.0特性&#xff0c;搜索schema&#xff0c;agg&#xff0c;表达式搜索映射&#xff0c;本文介绍onesearch 2.0 新特性, 参考第2节 规划特性与发布计划 1.2 关键词 文档 Document elasticsearch 一行数据称为…

【项目记录】登录认证(上)

前面已经实现了部门管理、员工管理的基本功能&#xff0c;但并没有登录&#xff0c;就直接访问到了Tlias智能学习辅助系统的后台。 这是不安全的&#xff0c;所以这次的主题就是登录认证。最终要实现的效果是&#xff1a; 如果用户名密码错误&#xff0c;不允许登录系统。 如…

Redis:安装与常用命令

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Redis &#x1f525; 安装 Redis 使⽤apt安装 apt install redis -y⽀持远程连接 修改 /etc/redis/redis.conf 修改 bind 127.0.0.1 为 bind 0.0.0.0 修改 protected-mode yes 为 protected-mo…

16-前端Web实战(Tlias案例-部门管理)

在前面的课程中&#xff0c;我们学习了Vue工程化的基础内容、TS、ElementPlus&#xff0c;那接下来呢&#xff0c;我们要通过一个案例&#xff0c;加强大家对于Vue项目的理解&#xff0c;并掌握Vue项目的开发。 这个案例呢&#xff0c;就是我们之前所做的Tlias智能学习辅助系统…

MagicAnimate 论文解读:引入时间一致性的视频人物动画生成方法

1. 前言/动机 问题&#xff1a;现有动画生成方法缺乏对时间信息的建模&#xff0c;常常出现时间一致性差的问题 描述&#xff1a; 现有的动画生成方法通常采用帧变形&#xff08;frame-warping&#xff09;技术&#xff0c;将参考图像变形以匹配目标动作。尽管这类方法能生成较…

C语言基础(09)【数组的概念 与一维数组】

数组 数组的概念 什么是数组 数组是相同类型、有序数据的集合。 数组的特征 数组中的数据称之为数组的元素(数组中的每一个匿名变量空间&#xff0c;是同构的)数组中的元素存放在内存空间建立。 衍生概念&#xff1a;下标&#xff08;索引&#xff09; 下标或者索引代表…

Spring MVC参数绑定终极手册:单多参/对象/集合/JSON/文件上传精讲

我们通过浏览器访问不同的路径&#xff0c;就是在发送不同的请求&#xff0c;在发送请求时&#xff0c;可能会带一些参数&#xff0c;本文将介绍了Spring MVC中处理不同请求参数的多种方式 一、传递单个参数 接收单个参数&#xff0c;在Spring MVC中直接用方法中的参数就可以&…

【Go-补充】Sync包

并发编程-Sync包 sync.WaitGroup 在代码中生硬的使用time.Sleep肯定是不合适的&#xff0c;Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法&#xff1a; 方法名功能(wg * WaitGroup) Add(delta int)计数器delta(wg *WaitGroup) Done()…

M-OFDM模糊函数原理及仿真

文章目录 前言一、M序列二、M-OFDM 信号1、OFDM 信号表达式2、模糊函数表达式 三、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、m-OFDM 模糊函数②、m-OFDM 距离分辨率③、m-OFDM 速度分辨率④、m-OFDM 等高线图 四、资源自取 前言 本文进行 M-OFDM 的原理讲解及仿真&#x…

《C++初阶之入门基础》【C++的前世今生】

【C的前世今生】目录 前言&#xff1a;---------------起源---------------一、历史背景二、横空出世---------------发展---------------三、标准立世C98&#xff1a;首个国际标准版本C03&#xff1a;小修订版本 四、现代进化C11&#xff1a;现代C的开端C14&#xff1a;对C11的…

长上下文推理新范式!QwenLong-L1如何通过强化学习突破大模型语境局限?

长上下文推理新范式&#xff01;QwenLong-L1如何通过强化学习突破大模型语境局限&#xff1f; 在大模型推理能力不断精进的今天&#xff0c;长上下文处理仍是亟待突破的难题。本文介绍的QwenLong-L1框架&#xff0c;借助渐进式语境扩展与强化学习&#xff0c;成功让大模型在长…

git 学习

目录 关于git 版本管理概述 git的优点 一 下载&#xff0c;安装git 二 使用git 的处理流程 三 学习基本的git命令 1 git初始化 2 查看 状态 3 提交的缓存区 4回退到工作区 关于git 版本管理概述 码云&#xff1a;Gitee - 基于 Git 的代码托管和研发协作平台 git 是用…

中国风展示工作总结商务通用PPT模版

中国风展示工作总结商务通用PPT模版&#xff1a;中国风商务通用PPT 模版https://pan.quark.cn/s/42ad18c010d4

gitflow

gitflow 1. 各个分支介绍 master分支: 源代码 HEAD始终反映出生产就绪状态。仅包含 生产环境可发布的代码&#xff0c;每个提交对应一个正式版本&#xff08;通过 git tag 打版本号&#xff09;一般情况下&#xff0c;只允许合并(如从 release 或 hotfix 分支合并), 禁止直接提…

Python训练营---Day42

DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业&#xff1a;理解下今天的代码即可 1、回调函数 回调函数&#xff08;Callback Function&#xff09;是一种特殊的函数&#xff0c;它作为参数传递给另一个函数&#…

Git远程操作

目录 1. 理解分布式版本控制系统 2. 远程仓库 3. 新建远程仓库 4. 克隆远程仓库 4.1 使用HTTPS方式&#xff1a; 4.2 使用SSH方式&#xff1a; 5. 向远程仓库推送 总结&#xff1a; 问题&#xff1a; 6. 拉取远程仓库 7. 配置Git 7.1 忽略特殊文件 8. 给命令配置别…

SolidWorks软件的安装与卸载

文章目录 软件的下载途径软件的安装软件的卸载 简介&#xff1a;这篇文章介绍了SolidWorks软件的安装与卸载&#xff0c;步骤是比较繁琐的&#xff0c;但照着步骤一步一步的来15分钟就能安装成功。这里要特别的注意一点的是&#xff0c;文件的安装位置一定要集中&#xff08;别…

Python 验证码识别(使用pytesseract库)

文章目录 摘要1、安装Tesseract-OCR2、在python中使用安装依赖 3、本地图片识别4、结合playwright动态识别网站验证码 摘要 python中使用pytesseract库进行ocr识别&#xff0c;需要安装Tesseract-OCR&#xff0c;通过指定pytesseract.tesseract_cmd路径&#xff0c;可以将esser…

日志与策略模式

什么是设计模式 IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式 日志认识 计算机中的日志是记录系统和软件运行中…

ToolsSet之:XML工具

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Text菜单下的XML Tool工具是一个Xml工…