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

news2025/7/26 20:11:16

摘要

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

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 {
    @Override
    public void handle(Context context) {
        System.out.println("当前状态:A,处理逻辑中...切换到状态B");
        context.setState(new ConcreteStateB());
    }
}

public class ConcreteStateB implements State {
    @Override
    public 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(); // 当前状态A
        context.request(); // 切换到B
        context.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
    @Autowired
    private List<ApprovalState> stateList;

    private Map<String, ApprovalState> stateMap;

    private ApprovalState currentState;

    @PostConstruct
    public 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 {

    @Override
    public void submit(ApprovalContext context) {
        System.out.println("当前状态:待初审 → 执行提交 → 切换到初审通过");
        context.setCurrentState("APPROVED_INIT");
    }

    @Override
    public void reject(ApprovalContext context) {
        System.out.println("当前状态:待初审 → 执行驳回 → 切换到终止状态");
        context.setCurrentState("TERMINATED");
    }

    @Override
    public String getStateCode() {
        return "WAIT_INIT";
    }
}

再如 “初审通过” 状态:

@Component
public class ApprovedInitialState implements ApprovalState {

    @Override
    public void submit(ApprovalContext context) {
        System.out.println("当前状态:初审通过 → 执行提交 → 切换到复审通过");
        context.setCurrentState("APPROVED_FINAL");
    }

    @Override
    public void reject(ApprovalContext context) {
        System.out.println("当前状态:初审通过 → 驳回 → 回到初审");
        context.setCurrentState("WAIT_INIT");
    }

    @Override
    public String getStateCode() {
        return "APPROVED_INIT";
    }
}

5.6. 🧪 控制层模拟调用

@RestController
@RequestMapping("/approval")
public class ApprovalController {

    @Autowired
    private 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.coloradmin.cn/o/2398984.html

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

相关文章

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;科学家们可能会用一种叫…

简单实现Ajax基础应用

Ajax不是一种技术&#xff0c;而是一个编程概念。HTML 和 CSS 可以组合使用来标记和设置信息样式。JavaScript 可以修改网页以动态显示&#xff0c;并允许用户与新信息进行交互。内置的 XMLHttpRequest 对象用于在网页上执行 Ajax&#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…

实现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;结果是绿…

Go的隐式接口机制

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

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

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

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

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

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

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

鸿蒙电脑会在国内逐渐取代windows电脑吗?

点击上方关注 “终端研发部” 设为“星标”&#xff0c;和你一起掌握更多数据库知识 10年内应该不会 用Windows、MacOS操作系统的后果是你的个人信息可能会被美国FBI看到&#xff0c;但绝大多数人的信息FBI没兴趣去看 你用某家公司的电脑系统,那就得做好被某些人监视的下场,相信…

持续领跑中国异地组网路由器市场,贝锐蒲公英再次登顶销量榜首

作为国产远程连接SaaS服务的创领者&#xff0c;贝锐持续引领行业发展&#xff0c;旗下贝锐蒲公英异地组网路由器&#xff0c;凭借出色的技术实力和市场表现&#xff0c;斩获2024年线上电商平台市场销量份额中国第一的佳绩&#xff0c;充分彰显了其在网络解决方案与异地组网领域…

Spring AI 系列3: Promt提示词

一、Promt提示词 Promt提示是引导 AI 模型生成特定输出的输入&#xff0c; 提示的设计和措辞会显著影响模型的响应。 在 Spring AI 中与 AI 模型交互的最低层级&#xff0c;处理提示有点类似于在 Spring MVC 中管理”视图”。 这涉及创建带有动态内容占位符的大段文本。 这些占…

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…