C++设计模式:状态模式(自动售货机)
什么是状态模式?
状态模式是一种行为型设计模式,它允许一个对象在其内部状态发生改变时,动态改变其行为。通过将状态相关的逻辑封装到独立的类中,状态模式能够将状态管理与行为解耦,从而让系统更加灵活和可维护。
通俗理解
想象一个自动售货机,它有以下几种状态:
- 无硬币状态:等待用户投入硬币。
- 有硬币状态:用户可以选择商品。
- 售货状态:用户选择商品后,售货机正在出货。
- 缺货状态:商品已售罄。
售货机的行为完全依赖于当前的状态,比如:
- 在无硬币状态下,用户无法选择商品。
- 在有硬币状态下,用户可以选择商品。
- 在售货状态下,售货机执行出货操作。
状态模式的核心就是:将状态逻辑抽象为独立的状态类,并通过上下文类(如售货机)动态切换状态对象,进而改变对象的行为。
状态模式的特点
优点
-
清晰封装状态逻辑:
- 每种状态的逻辑被集中到对应的状态类中,逻辑清晰且易于管理。
-
动态切换行为:
- 对象的行为可以通过切换状态动态改变,无需修改上下文类的代码。
-
扩展性强:
- 新增状态只需添加新的状态类,不影响现有代码,符合开闭原则。
缺点
-
类的数量增加:
- 每种状态都需要一个单独的类,可能导致类数量较多。
-
状态切换逻辑分散:
- 状态之间的切换逻辑分布在多个状态类中,增加了维护的复杂性。
状态模式的结构
UML类图
+---------------------+| Context | // 上下文类,管理当前状态+---------------------+| - state: State || + setState(state) || + request() |+---------------------+^|+---------------------+| State | // 抽象状态类+---------------------+| + handle() |+---------------------+^|+----------------------+ +----------------------+| ConcreteStateA | | ConcreteStateB | // 具体状态类+----------------------+ +----------------------+| + handle() | | + handle() |+----------------------+ +----------------------+
组成部分
-
State
(抽象状态类)- 定义了所有状态的通用接口,例如
insertCoin()
、selectProduct()
等。
- 定义了所有状态的通用接口,例如
-
ConcreteState
(具体状态类)- 实现
State
接口,定义与特定状态相关的行为。
- 实现
-
Context
(上下文类)- 持有一个状态对象,调用当前状态的行为。
- 负责在运行时切换状态对象。
案例:自动售货机
需求描述
设计一个简单的自动售货机,支持以下功能:
- 无硬币状态:用户不能选择商品。
- 有硬币状态:用户可以选择商品。
- 售货状态:用户选择商品后,售货机出货。
目标
- 实现售货机的状态管理,使得行为随着状态变化而动态改变。
- 状态切换应简单且易于扩展。
完整代码实现
以下是状态模式在自动售货机中的应用,输出为中文,附带详细注释。
#include <iostream>
#include <memory>
#include <string>// 抽象状态类
class State {
public:virtual void insertCoin() = 0; // 投入硬币virtual void selectProduct() = 0; // 选择商品virtual void dispenseProduct() = 0; // 出货virtual ~State() = default;
};// 前向声明:解决状态类互相引用的问题
class VendingMachine;
class HasCoinState;// 上下文类:自动售货机
class VendingMachine {
private:std::shared_ptr<State> currentState; // 当前状态public:void setState(std::shared_ptr<State> state) {currentState = state; // 切换状态}// 调用当前状态的方法void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};// 具体状态类:无硬币状态
class NoCoinState : public State {
private:VendingMachine* machine; // 上下文public:explicit NoCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬币已投入,进入有硬币状态。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine))); // 切换到有硬币状态}void selectProduct() override {std::cout << "请先投入硬币。" << std::endl;}void dispenseProduct() override {std::cout << "请先投入硬币并选择商品。" << std::endl;}
};// 具体状态类:有硬币状态
class HasCoinState : public State {
private:VendingMachine* machine;public:explicit HasCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬币已存在,请选择商品。" << std::endl;}void selectProduct() override {std::cout << "商品已选择,正在出货。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine))); // 模拟出货后切换到无硬币状态}void dispenseProduct() override {std::cout << "请先选择商品。" << std::endl;}
};// 客户端代码
int main() {// 创建自动售货机并设置初始状态VendingMachine machine;machine.setState(std::make_shared<NoCoinState>(&machine));// 测试售货机的功能std::cout << "=== 测试场景 1:无硬币状态 ===" << std::endl;machine.selectProduct(); // 未投硬币时选择商品machine.insertCoin(); // 投入硬币std::cout << "\n=== 测试场景 2:有硬币状态 ===" << std::endl;machine.selectProduct(); // 投币后选择商品return 0;
}
运行结果
=== 测试场景 1:无硬币状态 ===
请先投入硬币。
硬币已投入,进入有硬币状态。=== 测试场景 2:有硬币状态 ===
商品已选择,正在出货。
代码解读
1. 抽象状态类(State
)
class State {
public:virtual void insertCoin() = 0;virtual void selectProduct() = 0;virtual void dispenseProduct() = 0;virtual ~State() = default;
};
- 定义了所有状态的接口。
- 子类实现这些方法来处理具体的状态逻辑。
2. 上下文类(VendingMachine
)
class VendingMachine {
private:std::shared_ptr<State> currentState;public:void setState(std::shared_ptr<State> state) {currentState = state;}void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};
- 持有当前状态对象,并将行为委托给当前状态。
- 通过
setState
方法切换状态。
3. 具体状态类
- 无硬币状态:
void insertCoin() override {std::cout << "硬币已投入,进入有硬币状态。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine)));
}
- 有硬币状态:
void selectProduct() override {std::cout << "商品已选择,正在出货。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine)));
}
每个具体状态实现了当前状态的行为逻辑,同时可以切换到其他状态。
状态模式的应用场景
-
对象行为取决于状态:
- 自动售货机、订单状态管理、游戏角色状态(如站立、跑动、跳跃等)。
-
需要消除复杂条件判断:
- 替代
if-else
或switch
语句,将状态逻辑分散到独立的状态类中。
- 替代
总结
状态模式是一种非常实用的设计模式,它通过将状态逻辑封装到独立的类中,动态改变对象的行为,从而提高代码的扩展性和可维护性。
核心优势
- 行为动态改变:通过切换状态对象,动态改变对象的行为。
- 易扩展:新增状态只需添加状态类,不影响现有代码。
- 逻辑清晰:每个状态的逻辑集中在对应的状态类中,代码更易于理解和维护。
典型应用
- 自动售货机
- 在线订单状态(待支付、已支付、已发货等)
- 游戏角色状态(站立、奔跑、跳跃等)
相关文章:
C++设计模式:状态模式(自动售货机)
什么是状态模式? 状态模式是一种行为型设计模式,它允许一个对象在其内部状态发生改变时,动态改变其行为。通过将状态相关的逻辑封装到独立的类中,状态模式能够将状态管理与行为解耦,从而让系统更加灵活和可维护。 通…...

【网络安全实验室】脚本关实战详情
难道向上攀爬的那条路,不是比站在顶峰更让人热血澎湃吗 1.key又又找不到了 点击链接,burp抓包,发送到重放模块,点击go 得到key 2.快速口算 python3脚本 得到key 3.这个题目是空的 试了一圈最后发现是 4.怎么就是不弹出key呢…...

ts总结一下
ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…...

MySQL数据库笔记——主从复制
大家好,这里是Good Note,关注 公主号:Goodnote,本文详细介绍 MySQL的主从复制,从原理到配置再到同步过程。 文章目录 简介核心组件主从复制的原理作用主从复制的线程模型主从复制的模式形式复制的方式设计复制机制主从…...

OpenAI发布o3:圣诞前夜的AI惊喜,颠覆性突破还是技术焦虑?
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
欧拉-伯努利梁自由波动的频散关系
梁和杆都是一维结构,但是梁的弯曲波比杆的纵波要复杂多。例如即使最简单的欧拉-伯努利(Euler-Bernoulli)梁的弯曲波也具有频散特征,且当梁的特征尺寸和弯曲波波长满足某个比值时,欧拉-伯努利梁不再适用,需要引入铁摩辛克(Timoshenko)梁模型。 考察某一欧拉-伯努利梁,长度…...
Cursor小试1.生成一个网页的接口请求工具
一般开发过程中,会涉及到接口的调试,往往有时候开发的电脑不是我们自己的,没有安装一些类似postman 的接口调用工具,所以发现问题或者要测试某些接口是否正常的时候会很麻烦,而且现在网上也没有找到很好的免费的网页端接口请求的网址,所以我们使用Cursor来编写这样一个小工具, …...

Xilinx DCI技术
Xilinx DCI技术 DCI技术概述Xilinx DCI技术实际使用某些Bank特殊DCI要求 DCI级联技术DCI端接方式阻抗控制驱动器(源端接)半阻抗控制阻抗驱动器(源端接)分体式DCI(戴维宁等效端接到VCCO/2)DCI和三态DCI&…...
Kubernetes Pod 优雅关闭:如何让容器平稳“退休”?
Kubernetes Pod 优雅关闭:如何让容器平稳“退休”? 在 Kubernetes 中,Pod 是应用的基本单元。你可能会遇到需要停止某个 Pod 或容器的情况,可能是因为要更新、调整或故障恢复。在这种情况下,Pod 的优雅关闭࿰…...

鸿蒙应用开发(1)
可能以为通过 鸿蒙应用开发启航计划(点我去看上一节) 的内容,就足够了,其实还没有。 可是我还是要告诉你,你还需要学习新的语言 -- ArkTS。 ,ArkTS是HUAWEI开发的程序语言。你需要学习这门语言。这会花费你…...

SimForge HSF 案例分享|复杂仿真应用定制——UAVSim无人机仿真APP(技术篇)
导读 「神工坊」核心技术——「SimForge HSF高性能数值模拟引擎」支持工程计算应用的快速开发、自动并行,以及多域耦合、AI求解加速,目前已实现航发整机数值模拟等多个系统级高保真数值模拟应用落地,支持10亿阶、100w核心量级的高效求解。其低…...

使用 Adaptive Mesh Refinement 加速 CFD 仿真:最佳实践
CFD 仿真中的网格划分挑战 技术的进步正在增强设计探索,数值仿真在优化工程设计方面发挥着至关重要的作用。通常,计算流体动力学 (CFD) 仿真从定制的手工网格开始,具有精细和粗糙的区域,以平衡分辨率和单元…...

前端-动画库Lottie 3分钟学会使用
目录 1. Lottie地址 2. 使用html实操 3. 也可以选择其他的语言 1. Lottie地址 LottieFiles: Download Free lightweight animations for website & apps.Effortlessly bring the smallest, free, ready-to-use motion graphics for the web, app, social, and designs.…...
智能工厂的设计软件 应用场景的一个例子:为AI聊天工具添加一个知识系统 之5
本文要点 前端 问题描述语言 本文继续完善 “描述” ---现在我们应该可以将它称为 “问题problem描述语言 ”。 它 通过对话框的question 引发 表征的issue 的“涌现” 最终 厘清应用程序的“problem”。即它合并了 ISO七层模型中的上面三层,通过将三层 分别形成…...
java web
流程 1.浏览器发送http协议的格式数据和url给服务器软件tomcat 2.浏览器解析http格式数据并创建request和response对象,把数据封装到request对象里。 3.tomcat解析url确定访问路径,如果是静态资源html等,直接将html数据作为http格式响应体返回&#x…...
【嵌入式软件开发】嵌入式软件计时逻辑的两种实现:累加与递减的深入对比
本文主要从四个方面详细阐述了嵌入式软件编程中计时逻辑的两种实现方式:累加和递减。让我为您详细解析各个部分: 1. 基本概念对比 累加方式 从0开始向上计数每个周期增加固定值(通常为1)类似于我们日常生活中的秒表计时方式递减方式 从预设值开始向下计数每个周期减少固定…...

如何将vCenter6.7升级7.0?
vCenter是什么? vCenter是一种虚拟化管理软件,由VMware公司开发和发布。它是VMware vSphere虚拟化平台的核心组件之一,主要用于集中管理和监控虚拟化环境中的虚拟机、虚拟存储和网络资源。vCenter可以实现对多个ESXi主机的集中管理ÿ…...
服务器网卡绑定mode和交换机的对应关系
互联网各领域资料分享专区(不定期更新): Sheet 模式类别 网卡绑定mode共有七种(0~6): bond0、bond1、bond2、bond3、bond4、bond5、bond6 mode详解 mode0 ,即:(balance-rr) Round-robin policy(平衡轮循环策略,需要配置交换机静态聚合) mode…...

Maven (day04)
什么是maven? Maven 是 Apache 旗下的一个开源项目,是一款用于管理和构建 java 项目的工具。 官网:Welcome to Apache Maven – Maven https://maven.apache.org/ Maven的作用 依赖管理(方便快捷的管理项目依赖的资源(jar包)ÿ…...

Echart实现3D饼图示例
在可视化项目中,很多地方会遇见图表;echart是最常见的;这个示例就是用Echart, echart-gl实现3D饼图效果,复制即可用 //需要安装,再引用依赖import * as echarts from "echarts"; import echar…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...