C++实现设计模式---状态模式 (State)

状态模式 (State)

状态模式 是一种行为型设计模式,它允许对象在运行时根据内部状态的改变来动态改变其行为。通过将状态相关的行为封装到独立的类中,状态模式使得状态的切换更加清晰和灵活。


意图

  • 将对象的行为和状态分离,随着状态的改变动态调整对象的行为。
  • 避免使用大量的 if-elseswitch-case 语句处理状态逻辑。

使用场景

  1. 对象的行为依赖于状态
    • 例如订单的状态(未支付、已支付、已发货)。
  2. 需要动态切换状态
    • 例如游戏角色的动作(站立、行走、奔跑)。
  3. 需要扩展状态逻辑
    • 新增状态时不影响现有逻辑。

参与者角色

  1. 上下文类 (Context)
    • 保存当前状态的引用,提供接口供外部访问,并委托状态对象执行行为。
  2. 状态接口 (State)
    • 定义状态的公共接口。
  3. 具体状态类 (ConcreteState)
    • 实现状态接口并定义该状态下的具体行为。

代码示例

以下代码展示了一个机器人(Robot)的状态模式实现。机器人可以处于“待机状态 (Idle)”、“工作状态 (Working)”和“充电状态 (Charging)”之间切换。

#include <iostream>
#include <memory>
#include <string>// ------------------------------
// 状态接口:定义机器人状态的公共接口
class RobotContext; // 前置声明上下文类class RobotState {
public:virtual ~RobotState() = default; // 虚析构函数,确保多态销毁// 状态行为接口virtual void startWork(RobotContext& context) = 0;     // 开始工作virtual void chargeBattery(RobotContext& context) = 0; // 开始充电virtual std::string getStateName() const = 0;          // 获取状态名称
};// ------------------------------
// 上下文类:机器人
class RobotContext {
private:std::unique_ptr<RobotState> state; // 当前机器人状态,使用 unique_ptr 管理状态对象public:explicit RobotContext(std::unique_ptr<RobotState> initialState) : state(std::move(initialState)) {}// 设置新的状态void setState(std::unique_ptr<RobotState> newState) {state = std::move(newState);}// 委托开始工作行为给当前状态void startWork() {state->startWork(*this);}// 委托充电行为给当前状态void chargeBattery() {state->chargeBattery(*this);}// 获取当前状态名称std::string getStateName() const {return state->getStateName();}
};// ------------------------------
// 具体状态类:工作状态
class WorkingState : public RobotState {
public:void startWork(RobotContext& context) override {std::cout << "机器人已经在工作中,无法重复开始工作。
";}void chargeBattery(RobotContext& context) override {std::cout << "机器人停止工作,切换到充电状态。
";context.setState(std::make_unique<class ChargingState>()); // 切换到“充电状态”}std::string getStateName() const override {return "工作状态";}
};// ------------------------------
// 具体状态类:充电状态
class ChargingState : public RobotState {
public:void startWork(RobotContext& context) override {std::cout << "机器人充电完成,切换到工作状态。
";context.setState(std::make_unique<class WorkingState>()); // 切换到“工作状态”}void chargeBattery(RobotContext& context) override {std::cout << "机器人已经在充电中,无法重复充电。
";}std::string getStateName() const override {return "充电状态";}
};// ------------------------------
// 具体状态类:待机状态
class IdleState : public RobotState {
public:void startWork(RobotContext& context) override {std::cout << "机器人开始工作,切换到工作状态。
";context.setState(std::make_unique<class WorkingState>()); // 切换到“工作状态”}void chargeBattery(RobotContext& context) override {std::cout << "机器人开始充电,切换到充电状态。
";context.setState(std::make_unique<class ChargingState>()); // 切换到“充电状态”}std::string getStateName() const override {return "待机状态";}
};// ------------------------------
// 客户端代码
int main() {// 初始化机器人为“待机状态”RobotContext robot(std::make_unique<IdleState>());// 获取当前状态并输出std::cout << "当前机器人状态: " << robot.getStateName() << "
";// 机器人开始工作robot.startWork();std::cout << "当前机器人状态: " << robot.getStateName() << "
";// 机器人充电robot.chargeBattery();std::cout << "当前机器人状态: " << robot.getStateName() << "
";// 再次开始工作robot.startWork();std::cout << "当前机器人状态: " << robot.getStateName() << "
";return 0;
}

代码解析

1. 状态接口 (RobotState)
  • 定义公共接口
    • 包括 startWorkchargeBatterygetStateName 三个接口。
    • 每个具体状态类都必须实现这些接口,定义具体的行为。
2. 上下文类 (RobotContext)
  • 负责管理当前状态
    • RobotContext 保存当前状态的智能指针(std::unique_ptr),并负责管理状态对象的生命周期。
  • 行为委托
    • 调用 startWorkchargeBattery 时,行为委托给当前状态对象。
  • 状态切换
    • 通过 setState 方法切换状态,使用 std::make_unique 创建新的状态对象。
3. 具体状态类
  • IdleState (待机状态)
    • 允许切换到“工作状态”或“充电状态”。
  • WorkingState (工作状态)
    • 如果重复调用 startWork,提示“已在工作中”。
    • 支持切换到“充电状态”。
  • ChargingState (充电状态)
    • 如果重复调用 chargeBattery,提示“已在充电中”。
    • 支持切换到“工作状态”。
4. 状态切换逻辑
  • 每个状态类都可以通过 context.setState(std::make_unique<NewState>()); 切换到其他状态。
  • 切换状态时使用 std::unique_ptr 管理状态对象,确保内存安全。

运行结果

运行程序后,输出如下:

当前机器人状态: 待机状态
机器人开始工作,切换到工作状态。
当前机器人状态: 工作状态
机器人停止工作,切换到充电状态。
当前机器人状态: 充电状态
机器人充电完成,切换到工作状态。
当前机器人状态: 工作状态

优缺点

优点
  1. 封装状态逻辑
    • 每个状态的行为封装在独立的类中,方便维护和扩展。
  2. 动态切换状态
    • 通过 setState 动态切换状态,逻辑清晰。
  3. 消除条件语句
    • 使用多态机制避免了大量的 if-elseswitch-case 语句。
缺点
  1. 类数量增加
    • 每种状态都需要单独定义一个类,可能导致类数量较多。
  2. 上下文和状态耦合
    • 状态类依赖上下文类的操作。

适用场景

  1. 对象的行为依赖于状态
    • 如订单状态、文件操作等。
  2. 需要动态切换状态
    • 系统需要根据当前状态动态改变行为。

总结

状态模式通过将状态的行为封装到独立的类中,实现了对象的动态行为调整,是有限状态机的优雅实现方式。它特别适用于需要频繁切换状态且每种状态具有独立逻辑的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/68057.shtml

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

相关文章

202312 青少年软件编程等级考试C/C++ 一级真题答案及解析(电子学会)

第 1 题 数的输入和输出 输入一个整数和双精度浮点数,先将浮点数保留2位小数输出,然后输出整数。 时间限制:1000 内存限制:65536 输入 一行两个数,分别为整数N(不超过整型范围),双精度浮点数F,以一个空格分开。 输出 一行两个数,分别为保留2位小数输出的F,以…

信息系统项目管理-采购管理-采购清单示例

序号类别产品/服务名称规格/功能描述数量备注1硬件服务器高性能处理器&#xff0c;大容量存储10HP、DELL2网络设备高速路由器和交换机10华为3工作站多核处理器&#xff0c;高分辨率显示器25国产设备4移动检查设备手持式移动检查仪&#xff0c;可连接云平台30国产设备5打印机和扫…

继续以“实用”指导Pythonic编码(re通配表达式)(2024年终总结②)

弃现成工具手剥任务&#x1f9d0;&#xff0c;我哈哈滴就像笨笨的傻大个儿&#x1f60b;。 (笔记模板由python脚本于2025年01月12日 23:29:33创建&#xff0c;本篇笔记适合熟悉正则表达式的coder翻阅) 【学习的细节是欢悦的历程】 Python官网&#xff1a;https://www.python.or…

Mysql学习笔记之函数

1.简介 SQL提供了很多函数&#xff0c;便于在查询时能够快速的进行计算或者计数等操作&#xff0c;下文介绍一些实际场景中常用的函数。 2.字符串函数 常用的字符串函数如下表所示&#xff1a; 函数名描述LENGTH(str)返回字符串的长度&#xff0c;以字节为单位。例如LENGTH…

pytest 参数介绍

命令行参数描述常见使用案例-v / --verbose显示每个测试用例的详细信息&#xff0c;包括测试名称和状态pytest -v-s / --captureno禁用输出捕获&#xff0c;允许 print() 输出显示pytest -s-q / --quiet安静模式&#xff0c;减少输出&#xff0c;仅显示每个测试的通过/失败结果…

10步打造完美ASP.NET、Web API和控制台应用程序文件夹结构

一、前言 在大型项目中&#xff0c;合理的文件夹结构是项目成功的关键之一。一个好的文件夹结构就像是一座井然有序的图书馆&#xff0c;每一本书&#xff08;代码文件&#xff09;都有其固定的位置&#xff0c;让人能迅速找到所需。它可以让团队成员更容易理解和维护代码&…

conntrack iptables 安全组

centos 安装yum install conntrack-tools 1. conntrack状态 NEW: 新建连接&#xff08;第一次包&#xff09;。 ESTABLISHED: 已建立连接&#xff0c;正在传输数据。 RELATED: 与已有连接相关的连接&#xff0c;如 FTP 数据连接。 INVALID: 无效连接&#xff0c;无法识别或不…

相机和激光雷达的外参标定 - 无标定板版本

1. 实现的效果 通过本软件实现求解相机和LiDAR的外参&#xff0c;即2个传感器之间的三维平移[x, y, z]和三维旋转[roll, pitch, yaw]。完成标定后&#xff0c;可将点云投影到图像&#xff0c;效果图如下&#xff1a; 本软件的优势&#xff1a;&#xff08;1&#xff09;无需特…

WPF系列九:图形控件EllipseGeometry

简介 EllipseGeometry用于绘制一个椭圆的形状。它通常与其他图形元素结合使用&#xff0c;比如 Path 或者作为剪切区域来定义其他元素的外形。 定义椭圆&#xff1a;EllipseGeometry 用来定义一个椭圆或者圆的几何形状。参与绘制&#xff1a;可以被用作 Path 元素的数据&…

EFCore HasDefaultValueSql (续2 HasComputedColumnSql)

前情&#xff1a;EFCore HasDefaultValueSql EFCore HasDefaultValueSql (续1 ValueGeneratedOnAdd)-CSDN博客 小伙伴在使用 HasDefaultValueSql 时&#xff0c;对相关的 ValueGeneratedOnAdd, HasComputedColumnSql 也有了疑问&#xff1a; HasComputedColumnSql 对于计算…

qt设置qwidget背景色无效

最近在做一个界面&#xff0c;有三个子窗体&#xff0c;于是就把各个子窗体分别做成了三个UI&#xff0c;再将3个UI&#xff0c;放到1个UI&#xff0c;再将那一个UI在其他窗体上进行提升。 最后就发现怎么设置qwidget的背景都没有效果。 在Qt中&#xff0c;如果是给Qwidget的…

【Rust学习笔记】Rust 的所有权介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 博客内容主要围绕&#xff1a; 5G/6G协议讲解 高级C语言讲解 Rust语言讲解 文章目录 Rust中的所有权介绍1.1 一个简单的例子1.2 一个稍微复杂的例…

Centos9-SSH免密登录配置-修改22端口-关闭密码登录-提高安全性

Centos9-SSH免密登录配置-修改22端口-关闭密码登录 生成秘钥对将公钥信息存进authorized_keys测试登录查询访问记录、比对指纹更换22访问端口关闭账号密码登录生成秘钥对 生成密钥对,指定 备注 和 文件目录命令执行后,默认两次回车,不设置秘钥使用密码ssh-keygen -t rsa -b …

CentOS7下Hadoop集群分布式安装详细图文教程

1、集群规划 主机 角色 DSS20 NameNode DataNode ResourceManager NodeManager DSS21 SecondaryNameNode NameNode NodeManager DSS22 DataNode NodeManager 1.1、环境准备 1.1.1 关闭防火墙 #查看防火墙状态 firewall-cmd --state #停止…

Github 2025-01-11 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-11统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10C项目1Swift项目1Yazi - 快速终端文件管理器 创建周期:210 天开发语言:Rust协议类型:MIT LicenseStar数量:5668 个Fork数量:122…

在 Vue 项目中使用地区级联选

在 Vue 项目中使用地区级联选择的完整流程&#xff1a; 1.安装依赖包&#xff0c;这个包提供了中国省市区的完整数据。 npm install element-china-area-data --save 2.导入数据 import { regionData } from element-china-area-data 这个包提供了几种不同的数据格式&#…

深入学习 Python 爬虫:从基础到实战

深入学习 Python 爬虫&#xff1a;从基础到实战 前言 Python 爬虫是一个强大的工具&#xff0c;可以帮助你从互联网上抓取各种数据。无论你是数据分析师、机器学习工程师&#xff0c;还是对网络数据感兴趣的开发者&#xff0c;爬虫都是一个非常实用的技能。在本文中&#xff…

npm i 报错

nodejs中 使用npm install命令时报错 npm err! file C: \user\admin\package.json_package.json 里缺少 description 和 repository 两个n字段。-CSDN博客

基于改进粒子群优化的无人机最优能耗路径规划

目录 1. Introduction2. Preliminaries2.1. Particle Swarm Optimization Algorithm2.2. Deep Deterministic Policy Gradient2.3. Calculation of the Total Output Power of the Quadcopter Battery 3.OptimalEnergyConsumptionPathPlanningBasedonPSO-DDPG3.1.ProblemModell…

Redis为 List/Set/Hash 的元素设置单独的过期时间

一.业务简介 我们知道&#xff0c;Redis 里面暂时没有接口给 List、Set 或者 Hash 的 field 单独设置过期时间&#xff0c;只能给整个列表、集合或者 Hash 设置过期时间。 这样&#xff0c;当 List/Set/Hash 过期时&#xff0c;里面的所有 field 元素就全部过期了。但这样并不…