当前位置: 首页 > article >正文

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

摘要

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

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

相关文章:

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

摘要 状态设计模式是一种行为型设计模式&#xff0c;核心在于允许对象在内部状态改变时改变行为。它通过状态对象封装不同行为&#xff0c;使状态切换灵活清晰。该模式包含环境类、抽象状态类和具体状态类等角色&#xff0c;具有避免大量分支判断、符合单一职责和开闭原则等特…...

CppCon 2014 学习:Lightning Talk: Writing a Python Interpreter for Fun and Profit

Lightning Talk: Writing a Python Interpreter for Fun and Profit 这段内容在讲的是 Python 的执行模型&#xff0c;尤其是 CPython 的工作流程。下面是逐步解析&#xff1a; Python 是动态类型语言&#xff08;Dynamically typed&#xff09; 变量类型在运行时决定。x 4…...

CTFHub-RCE 命令注入-过滤运算符

观察源代码 代码里面可以发现过滤了运算符&#xff0c;我们可以尝试分号&#xff1b; 判断是Windows还是Linux 源代码中有 ping -c 4 说明是Linux 查看有哪些文件 127.0.0.1;ls 打开flag文件 cat这个php文件 127.0.0.1;cat flag_257413168915334.php 可是发现 文本内容显示…...

【音视频】H265 NALU分析

1 H265 概述 H264 与 H265 的区别 传输码率&#xff1a;H264 由于算法优化&#xff0c;可以低于 2Mbps 的速度实现标清数字图像传送&#xff1b;H.265 High Profile 可实现低于 1.5Mbps 的传输带宽下&#xff0c;实现 1080p 全高清视频传输。 编码架构&#xff1a;H.265/HEVC…...

运维 vm 虚拟机ip设置

虚拟网络设置 nat 模式 网卡 主机设置网卡地址 虚拟机绑定网卡...

飞牛fnNAS存储模式RAID 5数据恢复

目录 一、添加硬盘 二、创建RAID 5 存储空间 三、上传测试文件 四、拆除硬盘 五、更换硬盘 六、修复RAID 5 七、验证其内文件 八、NAS系统崩溃后的数据盘 前文《飞牛fnNAS存储空间模式详解》 中介绍了fnNAS存储空间的几个模式,细心的网友应该能感受到,我是非常推崇R…...

论文笔记:DreamDiffusion

【初中生也能看得懂的讲解】 想象一下&#xff0c;我们能不能直接用“脑子想”来画画&#xff1f;比如你想到一只猫&#xff0c;电脑就能画出一只猫。这听起来是不是很酷&#xff1f;科学家们一直在努力实现这个“意念画画”的梦想。 以前&#xff0c;科学家们可能会用一种叫…...

户外摄像头监控如何兼顾安全实时监控

一、技术手段提升隐私安全性 硬件与功能设计 采用支持隐私保护技术的设备&#xff0c;例如带电子开关的摄像头&#xff08;可远程控制摄像头启闭&#xff09;3&#xff0c;或搭载本地AI算法的设备&#xff0c;仅识别人形、车辆等目标&#xff0c;减少无关信息采集。 使用安全…...

Neo4j 备份与恢复:原理、技术与最佳实践

在数据驱动的应用中&#xff0c;图数据库Neo4j承载着至关重要的关联数据。确保其数据安全与业务连续性依赖于强大的备份与恢复策略。本文将深入探讨Neo4j备份恢复的核心原理、关键技术、实用技巧及行业最佳实践&#xff0c;内容基于官方最新文档。 构建健壮的 Neo4j 备份恢复体…...

简单实现Ajax基础应用

Ajax不是一种技术&#xff0c;而是一个编程概念。HTML 和 CSS 可以组合使用来标记和设置信息样式。JavaScript 可以修改网页以动态显示&#xff0c;并允许用户与新信息进行交互。内置的 XMLHttpRequest 对象用于在网页上执行 Ajax&#xff0c;允许网站将内容加载到屏幕上而无需…...

关于 java:3. Java 常用类库与数据结构

一、String 1.1 String 是什么&#xff1f; public final class String implements java.io.Serializable, Comparable<String>, CharSequence特点&#xff1a; 是 不可变对象&#xff08;immutable&#xff09; 是 final 类&#xff0c;不能被继承 内部使用 字符数组…...

数据挖掘顶刊《IEEE Transactions on Knowledge and Data Engineering》2025年5月研究热点都有些什么?

本推文对2025年5月出版的数据挖掘领域国际顶级期刊《IEEE Transactions on Knowledge and Data Engineering》进行了分析&#xff0c;对收录的62篇论文的关键词与研究主题进行了汇总&#xff0c;并对其中的研究热点进行了深入分析&#xff0c;希望能为相关领域的研究人员提供有…...

LabVIEW双光子显微镜开发

基于LabVIEW 开发高性能双光子显微镜系统&#xff0c;聚焦于生物样本深层成像与纳米材料三维表征。实现了超快激光控制、多维数据采集与实时图像重建。系统采用飞秒激光光源与高精度振镜扫描模块&#xff0c;结合 LabVIEW 的 FPGA 实时控制能力&#xff0c;可对活体组织、荧光纳…...

WordPress 6.5版本带来的新功能

WordPress 6.5正式上线了&#xff01;WordPress团队再一次为我们带来了许多新的改进。在全球开发者的共同努力下&#xff0c;WordPress推出了许多新的功能&#xff0c;本文将对其进行详细总结。 Hostease的虚拟主机现已支持一键安装最新版本的WordPress。对于想要体验WordPres…...

将材质球中的纹理属性对应的贴图保存至本地

通过Texture2D的EncodeToPNG方法将纹理转为图片形式 material.GetTexture方法通过属性名获取纹理贴图 material.SetTexture方法通过属性名设置纹理贴图 属性名可在shader代码中查看 using UnityEngine; using System.IO;public class TextureSaver : MonoBehaviour {public…...

Linux应用开发之网络套接字编程

套接字&#xff08;Socket&#xff09;是计算机网络数据通信的基本概念和编程接口&#xff0c;允许不同主机上的进程&#xff08;运行中的程序&#xff09;通过网络进行数据交换。它为应用层软件提供了发送和接收数据的能力&#xff0c;使得开发者可以在不用深入了解底层网络细…...

实现RabbitMQ多节点集群搭建

目录 引言 一、环境准备 二、利用虚拟机搭建 ​ 三、镜像集群配置 四、HAProxy实现负载均衡(主用虚拟机操作) 五、测试RabbitMQ集群搭建情况 引言 在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色,而 RabbitMQ 作为…...

GLIDE论文阅读笔记与DDPM(Diffusion model)的原理推导

Abstract 扩散模型&#xff08;Diffusion model&#xff09;最近被证明可以生成高质量的合成图像&#xff0c;尤其是当它们与某种引导技术结合使用时&#xff0c;可以在生成结果的多样性与保真度之间进行权衡。本文探讨了在文本条件图像生成任务中使用扩散模型&#xff0c;并比…...

机器学习——放回抽样

为了构建树集成模型&#xff0c;需要一种叫做有放回采样的技术。 以4个标记为演示&#xff0c;分别是红色、黄色、绿色和蓝色&#xff0c;用一个黑色的袋子把这四个标记的例子放进去&#xff0c;然后从这个袋子里有放回地抽取四次&#xff0c;抽出一个标记&#xff0c;结果是绿…...

前端内存泄漏:原理、检测与防范实践

一、什么是内存泄漏 内存泄漏&#xff08;Memory Leak&#xff09;是指程序中已动态分配的堆内存由于某种原因未能被释放或无法被释放&#xff0c;造成系统内存的浪费&#xff0c;导致程序运行速度减慢甚至系统崩溃等严重后果。 在前端开发中&#xff0c;虽然现代浏览器具备垃…...

Go的隐式接口机制

正确使用Interface 不要照使用C/Java等OOP语言中接口的方式去使用interface。 Go的Interface的抽象不仅可以用于dynamic-dispatch 在工程上、它最大的作用是&#xff1a;隔离实现和抽象、实现完全的dependency inversion 以及interface segregation(SOLID principle中的I和D)。…...

UE音频中间件wwise插件

虚幻引擎用wwise插件有什么用? 没有这个插件不是也能播放声音吗? 为什么要用他? 在Unreal Engine&#xff08;UE&#xff09;中使用 Wwise 插件&#xff0c;不是因为 UE 不能做声音&#xff0c;而是因为 Wwise 更强、更专业&#xff0c;适合复杂的音频需求。 &#x1f3a7; …...

C++.cstring string

C.cstring string 1. C 中的字符串概述1.1 C 中字符串的两种表示方式C 中的 cstring示例代码 C 中的 string示例代码 1.2 C 中字符串的使用场景使用 cstring 的场景使用 string 的场景示例对比使用 cstring 的示例使用 string 的示例 2. C 中的 cstring2.1 cstring 的定义与基本…...

Spring AOP 和 AspectJ 有什么区别

1. 织入方式 Spring AOP&#xff1a; 运行时织入&#xff1a;Spring AOP 使用动态代理技术&#xff08;如 JDK 动态代理或 CGLIB 代理&#xff09;在运行时创建代理对象。 依赖 Spring 容器&#xff1a;仅支持 Spring 管理的 Bean。 AspectJ&#xff1a; 编译时织入&#xf…...

报表/报告组件(二)-实例与实现解释

上篇《报表/报告组件(一)-指标/属性组件设计》介绍了组件核心指标/属性设计&#xff0c;本文以实例介绍各个特性的实现和效果&#xff0c;实例是多个报告融合&#xff0c;显示所有的特性。 设计 指标/属性组件是报告/报表关键部分&#xff0c;上篇已介绍过&#xff0c;本节回顾…...

linux的实时性

Linux 的实时性取决于其内核配置和使用场景。标准 Linux 内核&#xff08;非实时内核&#xff09;在设计上更注重吞吐量和公平调度&#xff0c;而非严格的实时性。但通过以下方式可以显著提升其实时性能&#xff1a; 1. 标准 Linux 内核的实时性 优点&#xff1a; 适用于大多数…...

Opencv4 c++ 自用笔记 04 图像滤波与边缘检测

图像滤波与边缘检测 直接采集到的图像可能带有噪声的干扰&#xff0c;因此去除噪声是图像预处理中十分重要的一步。图像滤波是图像噪声去除的重要方式。 图像卷积 卷积操作广泛应用于信号处理领域&#xff0c;而图像本质上可以视为一种二维信号数据。 卷积过程可以理解为一…...

流媒体基础解析:音视频封装格式与传输协议

在视频处理与传输的完整流程中&#xff0c;音视频封装格式和传输协议扮演着至关重要的角色。它们不仅决定了视频文件的存储方式&#xff0c;还影响着视频在网络上的传输效率和播放体验。今天&#xff0c;我们将深入探讨音视频封装格式和传输协议的相关知识。 音视频封装格式 什…...

一个html实现数据库自定义查询

使用场景 应用上线后甲方频繁的找开发查询数据库数据&#xff0c;且没有固定的查询规律&#xff0c;产品经理也没有规划报表需求。 实现方案 后端开放自定义sql查询&#xff0c;屏蔽所有数据库的高危操作&#xff0c;将常用查询的sql放在一个html中的js中直接查询&#xff0…...

OCC笔记:TopoDS_Edge上是否一定存在Geom_Curve

1. 问题 写occt代码时&#xff0c;访问边的几何数据&#xff0c;通常有以下代码&#xff0c;若边不为空&#xff0c;BRep_Tool::Curve函数是否能返回Curve的有效对象指针呢&#xff1f; //其他略...const TopoDS_Edge& curEdge TopoDS::Edge(edgeExp.Current()); if( cu…...