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

摘要

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

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.pswp.cn/news/908325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 观察者模式:设计与实现详解

一、引言 在现代软件开发中,组件间的交互与通信是系统设计的核心挑战之一。观察者模式(Observer Pattern)作为一种行为设计模式,提供了一种优雅的解决方案,用于实现对象间的一对多依赖关系。本文将深入探讨 C++ 中观察者模式的设计理念、实现方式及其应用场景。 二、观察…

Windows 账号管理与安全指南

Windows 账号管理与安全指南 概述 Windows 账号管理是系统安全的基础&#xff0c;了解如何正确创建、管理和保护用户账户对于系统管理员和安全专业人员至关重要。本文详细介绍 Windows 系统中的账户管理命令、隐藏账户创建方法以及安全防护措施。 基础账户管理命令 net use…

[蓝桥杯]摆动序列

摆动序列 题目描述 如果一个序列的奇数项都比前一项大&#xff0c;偶数项都比前一项小&#xff0c;则称为一个摆动序列。即 a2i<a2i−1,a2i1 >a2ia2i​<a2i−1​,a2i1​ >a2i​。 小明想知道&#xff0c;长度为 mm&#xff0c;每个数都是 1 到 nn 之间的正整数的…

Python 网络编程 -- WebSocket编程

作者主要是为了用python构建实时网络通信程序。 概念性的东西越简单越好理解,因此,下面我从晚上摘抄的概念 我的理解。 什么是网络通信? 更确切地说&#xff0c;网络通信是两台计算机上的两个进程之间的通信。比如&#xff0c;浏览器进程和新浪服务器上的某个Web服务进程在通…

GM DC Monitor如何实现TCP端口状态监控-操作分享

本节讲解如何通过现有指标提取监控脚本制作自定义的TCP端口监控指标 一、功能介绍 通过提取已有的监控指标的监控命令&#xff0c;来自定义TCP端口的监控指标。 二、配置端口监控 1&#xff09;定位监控脚本 确定脚本及参数如下&#xff1a; check_protocol_tcp.pl --plug…

LabVIEW与Modbus/TCP温湿度监控系统

基于LabVIEW 开发平台与 Modbus/TCP 通信协议&#xff0c;设计一套适用于实验室环境的温湿度数据采集监控系统。通过上位机与高精度温湿度采集设备的远程通信&#xff0c;实现多设备温湿度数据的实时采集、存储、分析及报警功能&#xff0c;解决传统人工采集效率低、环境适应性…

Ntfs!ReadIndexBuffer函数分析之nt!CcGetVirtualAddress函数之nt!CcGetVacbMiss

第一部分&#xff1a; NtfsMapStream( IrpContext, Scb, LlBytesFromIndexBlocks( IndexBlock, Scb->ScbType.Index.IndexBlockByteShift ), Scb->ScbType.Index.BytesPerIndexBuffer, &am…

vite+vue3项目中,单个组件中使用 @use报错

报错信息&#xff1a; [plugin:vite:css] [sass] use rules must be written before any other rules.use 官方说明 注意事项&#xff1a; https://sass-lang.com/documentation/at-rules/use/ 样式表中的 use 规则必须位于所有其他规则&#xff08;除 forward 外&#xff0…

基于VMD-LSTM融合方法的F10.7指数预报

F10.7 Daily Forecast Using LSTM Combined With VMD Method ​​F10.7​​ solar radiation flux is a well-known parameter that is closely linked to ​​solar activity​​, serving as a key index for measuring the level of solar activity. In this study, the ​​…

React 新项目

使用git bash 创建一个新项目 建议一开始就创建TS项目 原因在Webpack中改配置麻烦 编译方法:ts compiler 另一种 bable 最好都配置 $ create-react-app cloundmusic --template typescript 早期react项目 yarn 居多 目前npm包管理居多 目前pnpm不通用 icon 在public文件夹中…

2025年- H65-Lc173--347.前k个高频元素(小根堆,堆顶元素是当前堆元素里面最小的)--Java版

1.题目描述 2.思路 &#xff08;1&#xff09;这里定义了一个小根堆&#xff08;最小堆&#xff09;&#xff0c;根据元素的频率从小到大排序。小根堆原理&#xff1a;堆顶是最小值&#xff0c;每次插入或删除操作会保持堆的有序结构&#xff08;常用二叉堆实现&#xff09;。 …

VR/AR 显示瓶颈将破!铁电液晶技术迎来关键突破

在 VR/AR 设备逐渐走进大众生活的今天&#xff0c;显示效果却始终是制约其发展的一大痛点。纱窗效应、画面拖影、眩晕感…… 传统液晶技术的瓶颈让用户体验大打折扣。不过&#xff0c;随着铁电液晶技术的重大突破&#xff0c;这一局面有望得到彻底改变。 一、传统液晶技术瓶颈…

【bug】Error: /undefinedfilename in (/tmp/ocrmypdf.io.9xfn1e3b/origin.pdf)

在使用ocrmypdf的时候&#xff0c;需要Ghostscript9.55及以上的版本&#xff0c;但是ubuntu自带为9.50 然后使用ocrmypdf报错了 sudo apt update sudo apt install ghostscript gs --version 9.50 #版本不够安装的版本为9.50不够&#xff0c;因此去官网https://ghostscript.c…

【TinyWebServer】线程同步封装

目录 POSIX信号量 int sem_init(sem_t* sem,int pshared,unsingned int value); int sem_destroy(sem_t* sem); int sem_wait(sem_t* sem); int sem_post(sem_t* sem); 互斥量 条件变量 为了对多线程程序实现同步问题&#xff0c;可以用信号量POSIX信号量、互斥量、条件变…

打造高效多模态RAG系统:原理与评测方法详解

引言 随着信息检索与生成式AI的深度融合&#xff0c;检索增强生成&#xff08;RAG, Retrieval-Augmented Generation&#xff09; 已成为AI领域的重要技术方向。传统RAG系统主要依赖文本数据&#xff0c;但真实世界中的信息往往包含图像、表格等多模态内容。多模态RAG&#xf…

Unity安卓平台开发,启动app并传参

using UnityEngine; using System;public class IntentReceiver : MonoBehaviour {public bool isVR1;void Start(){Debug.LogError("app1111111111111111111111111");if (isVR1){LaunchAnotherApp("com.HappyMaster.DaKongJianVR2");}else{// 检查是否有传…

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】 目录 云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】1.RPM包的一般安装位置2.软件名和软件包名3.查询软件信息4.查询软件包5.导入红帽签名信息&#xff0c;解决查询软件包信息报错6.利用…

【图像处理3D】:点云图是怎么生成的

点云图是怎么生成的 **一、点云数据的采集方式****1. 激光雷达&#xff08;LiDAR&#xff09;****2. 结构光&#xff08;Structured Light&#xff09;****3. 双目视觉&#xff08;Stereo Vision&#xff09;****4. 飞行时间相机&#xff08;ToF Camera&#xff09;****5. 其他…

javaweb -html -CSS

HTML是一种超文本标记语言 超文本&#xff1a;超过了文本的限制&#xff0c;比普通文本更强大&#xff0c;除了文字信息&#xff0c;还可以定义图片、音频、视频等内容。 标记语言&#xff1a;由标签"<标签名>"构成的语言。 CSS:层叠样式表&#xff0c;用于…

pyinstaller 安装 ubuntu

安装命令 pip install pyinstaller 读取安装路径 ➜ ~ find ~/.local/ -name pyinstaller/home/XXX/.local/bin/pyinstaller 路径配置 vi ~/.zshrc 添加到文件最后 export PATH"$PATH:/home/XXX/.local/bin/" 查看版本号 ➜ ~ source ~/.zshrc➜ ~ pyi…