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…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
