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

状态模式(State Pattern)

C++ 状态模式(State Pattern)

一、模式概述

状态模式(State Pattern)属于行为型设计模式

核心思想

将对象的不同状态分别封装为独立类,对象行为随内部状态改变而自动变化,对外表现如同修改了对象所属类;彻底替代传统大量if-else/switch分支判断,让状态逻辑解耦、职责清晰。

适用场景

  1. 对象行为依赖自身状态,运行时状态改变则行为随之改变;
  2. 业务中存在大量基于状态的条件判断代码,分支臃肿、难以维护;
  3. 状态数量固定、状态转换规则明确,需要扩展新状态;
  4. 典型业务:电梯状态、订单流程、网络连接、游戏角色、设备运行状态机等。

二、核心角色

状态模式由三类核心角色组成,各司其职:

角色名称职责说明
抽象状态State定义所有状态统一的行为接口,声明状态处理方法,通常为纯虚类
具体状态ConcreteStateX继承抽象状态,实现当前状态独有行为;负责触发状态跳转
上下文Context持有当前状态对象;对外提供统一调用入口;提供状态切换接口,串联整个状态流程

角色协作流程

  1. 客户端调用上下文的对外接口;
  2. 上下文将请求委托给当前具体状态对象处理;
  3. 具体状态执行自身逻辑,并根据业务规则修改上下文的状态;
  4. 下次请求会交由新状态处理,实现行为自动切换。

三、模式优缺点

优点

  1. 消除分支语句:剥离大量if-else/switch,代码结构更整洁;
  2. 符合开闭原则:新增状态只需新增具体状态类,无需修改原有代码;
  3. 状态逻辑隔离:每个状态的行为、转换逻辑内聚在对应类中,职责单一,易维护;
  4. 状态转换规范化:转换规则统一管理,流程清晰可控。

缺点

  1. 类数量膨胀:状态越多,对应的具体状态类就越多,增加代码体量;
  2. 流程分散:状态跳转逻辑分散在各个状态类中,整体状态流转流程阅读难度提升;
  3. 过度设计:简单状态(仅2~3个状态、逻辑简单)使用该模式会增加复杂度。

四、C++ 基础实现(标准写法)

1. 前置说明

  • 使用纯虚类作为抽象状态基类;
  • 采用原始指针演示基础逻辑,后文补充智能指针工程写法;
  • 类之间存在交叉引用,使用前向声明解决头文件循环依赖。

2. 完整代码示例

#include<iostream>usingnamespacestd;// 前向声明上下文类classContext;// ===================== 抽象状态类 State =====================classState{public:// 纯虚函数:状态处理行为,参数为上下文,用于状态切换virtualvoidhandle(Context*context)=0;virtual~State()=default;// 虚析构,保证多态析构安全};// ===================== 具体状态类 A =====================classConcreteStateA:publicState{public:voidhandle(Context*context)override;};// ===================== 具体状态类 B =====================classConcreteStateB:publicState{public:voidhandle(Context*context)override;};// ===================== 上下文类 Context =====================classContext{private:State*m_currentState;// 持有当前状态对象public:// 构造函数:初始化默认状态Context(State*state):m_currentState(state){}// 设置/切换状态voidsetState(State*state){m_currentState=state;}// 对外统一请求接口voidrequest(){if(m_currentState){m_currentState->handle(this);// 委托给当前状态处理}}};// 状态A 实现逻辑:执行行为,并切换到状态BvoidConcreteStateA::handle(Context*context){cout<<"当前处于【状态A】,执行状态A逻辑"<<endl;// 状态跳转:切换为状态Bcontext->setState(newConcreteStateB());}// 状态B 实现逻辑:执行行为,并切换到状态AvoidConcreteStateB::handle(Context*context){cout<<"当前处于【状态B】,执行状态B逻辑"<<endl;// 状态跳转:切换为状态Acontext->setState(newConcreteStateA());}// ===================== 客户端测试 =====================intmain(){// 初始化上下文,默认状态为AContextctx(newConcreteStateA());// 多次调用,观察状态自动切换ctx.request();ctx.request();ctx.request();return0;}

3. 运行结果

当前处于【状态A】,执行状态A逻辑 当前处于【状态B】,执行状态B逻辑 当前处于【状态A】,执行状态A逻辑

五、C++ 工程级优化(智能指针版)

原始指针容易造成内存泄漏、野指针,正式项目推荐使用std::unique_ptr管理状态对象。

优化后代码

#include<iostream>#include<memory>usingnamespacestd;classContext;// 抽象状态classState{public:virtualvoidhandle(Context*context)=0;virtual~State()=default;};// 具体状态AclassConcreteStateA:publicState{public:voidhandle(Context*context)override;};// 具体状态BclassConcreteStateB:publicState{public:voidhandle(Context*context)override;};// 上下文(使用 unique_ptr 管理状态)classContext{private:unique_ptr<State>m_currentState;public:explicitContext(unique_ptr<State>state):m_currentState(move(state)){}voidsetState(unique_ptr<State>state){m_currentState=move(state);}voidrequest(){if(m_currentState)m_currentState->handle(this);}};voidConcreteStateA::handle(Context*context){cout<<"当前处于【状态A】,执行逻辑"<<endl;context->setState(make_unique<ConcreteStateB>());}voidConcreteStateB::handle(Context*context){cout<<"当前处于【状态B】,执行逻辑"<<endl;context->setState(make_unique<ConcreteStateA>());}// 客户端intmain(){Contextctx(make_unique<ConcreteStateA>());ctx.request();ctx.request();ctx.request();return0;}

优势:智能指针自动释放内存,无需手动delete,规避内存泄漏。

六、两种状态切换方式

在实际开发中,状态切换分为两种模式:

1. 状态类内部控制跳转(常用)

  • 跳转逻辑写在ConcreteState::handle中;
  • 优点:状态自管理,内聚性强;
  • 缺点:跳转逻辑分散。

2. 上下文统一控制跳转

  • 所有状态转换规则集中写在Context中;
  • 优点:整体状态流转一目了然,便于梳理完整流程;
  • 缺点:上下文会随状态增多变得臃肿。

七、状态模式 vs 策略模式(易混区分)

两者结构相似,均封装行为、委托执行,核心区别在行为目的与切换逻辑

对比维度状态模式策略模式
核心目的处理对象状态变化,行为随状态自动改变封装多种算法/策略,按需替换算法
切换触发内部状态流转,自动切换外部客户端主动选择,手动切换
关系状态之间存在流转、依赖关系各个策略相互独立,无先后流转关系
典型场景状态机、订单、电梯、设备状态排序算法、支付方式、加密算法

八、使用注意事项

  1. 控制状态数量:状态过多会产生大量子类,若状态超过10个,可考虑改用配置表+枚举实现状态机;
  2. 循环依赖处理ContextState互相引用,必须使用前向声明
  3. 析构安全:抽象状态基类必须提供虚析构函数,防止多态析构内存泄漏;
  4. 慎用场景:简单两状态、固定逻辑,不建议强行使用,避免过度设计;
  5. 多线程场景:多线程下需对m_currentState加锁,保证状态切换线程安全。

九、总结

  1. 本质:把状态变成类,用多态替代分支判断
  2. 核心三角色:抽象状态、具体状态、上下文;
  3. C++ 开发优先使用智能指针管理状态对象,保证内存安全;
  4. 适合复杂状态机场景,简单场景按需取舍。
http://www.jsqmd.com/news/894055/

相关文章:

  • 别再只会转格式了!FFmpeg的-i、-f、-ss参数组合,5分钟搞定视频精准裁剪与格式转换
  • LM Studio本地大模型实战指南:零基础部署、RAG优化与生产API配置
  • 通过taotoken用量看板分析并优化ai应用月度消耗的实践
  • 51单片机PWM调速避坑指南:为什么你的电机抖动、不转或烧芯片?从驱动电路到代码的常见问题排查
  • GNURadio实战:一台电脑插两个RTL-SDR电视棒,同时收听不同FM电台的完整配置流程
  • DeepSeek V4 Pro 永久降价:AI 模型价格战背后的技术逻辑与开发者的新机遇
  • 别再死记硬背了!用UE4 DS做联机游戏,搞懂Role和Replication这一篇就够了
  • 观察使用Taotoken后API调用的成功率和响应时间变化
  • LM Studio本地大模型实战指南:免CLI开箱即用
  • [吐槽] outlook 新版本
  • 从零打包一个Ubuntu软件:详解deb包里那个必不可少的control文件怎么写
  • 手把手教你用STM32看懂充电桩的‘暗号’:从CP信号到充电引导的完整解析
  • 探索型与执行型AI智能体:设计哲学、技术实现与协同工作流
  • 告别臃肿SDK:手把手教你为RK3568开发板单独编译Linux 4.19内核(附完整脚本)
  • O4-Mini轻量大模型API实战:边缘部署与工业诊断落地指南
  • C++26概述
  • SQL级联删除ON DELETE CASCADE原理与实战避坑指南
  • Unity ShaderGraph Input节点实战:用UV和Time节点5分钟做出流动水面效果
  • 避开国内网络大坑:手把手教你用清华源和本地包搞定DiffDock环境配置(含dllogger、openfold等疑难杂症解决)
  • 避坑指南:Unity用C#获取系统时间,别忘了时区、性能和格式化这三点!
  • 2026干混砂浆源头直供技术解析与靠谱供应商参考:成都水泥厂家/成都河沙批发/拉法基水泥厂家推荐四川干混砂浆生产厂家/选择指南 - 优质品牌商家
  • Keil C51内存布局控制:指针数组与字符串常量地址固定技巧
  • 数据归一化实战指南:解决特征量纲不一致与模型失效问题
  • Unity编辑器Selection系统深度解析与避坑指南
  • 当每一行代码都可能是“AI代笔”:你会为“零AI介入”的汽车支付溢价吗?
  • SAP MIRO发票校验时,如何用增强LMR1M001自动拦截供应商信息错误?
  • LLM安全攻防:对抗攻击原理与防御实践
  • 2026年Q2智慧酒店OLT光网系统专业厂家排行:智慧酒店RCU客房控制系统、智慧酒店升级改造方案及报价、智慧酒店客房系统选择指南 - 优质品牌商家
  • QMCDecode终极指南:免费快速解锁QQ音乐加密格式的完整教程
  • 从地理空间数据云到可游玩地图:一份给独立开发者的真实世界地形创建全流程指南