重拾设计模式--状态模式
文章目录
- 状态模式(State Pattern)概述
- 状态模式UML图
- 作用:
- 状态模式的结构
- 环境(Context)类:
- 抽象状态(State)类:
- 具体状态(Concrete State)类:
- C++ 代码示例1
- C++示例代码2
- 应用场景
- 游戏开发领域
- 图形用户界面(GUI)系统
- 工作流系统和业务流程管理
- 网络协议和通信系统
状态模式(State Pattern)概述
定义:
状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为。就好像对象看起来修改了它的类一样,通过将不同状态对应的行为封装到不同的状态类中,让一个对象在不同的状态下可以表现出不同的行为,而这些行为的切换是由对象的状态变化来驱动的。
状态模式UML图

作用:
提高代码的可维护性和可扩展性:当一个对象有多种状态,且每种状态下行为差异较大时,如果使用大量的条件判断语句(如if-else或者switch语句)来处理不同状态下的行为,代码会变得复杂且难以维护。而状态模式把不同状态的行为分散到各个对应的状态类中,后续添加新状态或者修改某个状态下的行为就比较方便,只需对相应的状态类进行操作,不会影响到其他部分的代码。
符合开闭原则:对扩展开放,对修改关闭。例如在一个游戏角色有多种状态(站立、行走、攻击等)的场景中,若要新增一种 “跳跃” 状态以及对应的行为,只需要新增一个 “跳跃” 状态类实现相关行为逻辑,而不用去大规模修改原有的代码结构,原有的状态类和使用状态的对象代码都可以保持相对稳定。
增强代码的可读性:不同状态下的行为逻辑清晰地封装在各自的状态类中,阅读代码时可以更直观地了解每个状态对应的具体操作,相比于把所有状态相关行为混杂在一个类里的写法,结构更加清晰明了。
状态模式的结构
环境(Context)类:
它定义了客户端感兴趣的接口,并且维护一个具体状态类的实例,这个实例代表了当前对象所处的状态。环境类的行为会受到其内部状态对象的影响,它会将请求委托给当前状态对象来处理。
抽象状态(State)类:
它定义了一个接口,这个接口包含了那些在不同具体状态下对象可能执行的方法,所有的具体状态类都要实现这个抽象状态类所定义的接口,来提供各自状态下的具体行为逻辑。
具体状态(Concrete State)类:
实现了抽象状态类中定义的接口,针对具体的某一种状态,提供了该状态下相应行为的具体实现。每个具体状态类实现的行为会根据具体业务需求而不同,当环境类的状态切换到某个具体状态时,环境类发出的请求就会由对应的这个具体状态类来处理。
C++ 代码示例1
以下以一个简单的电梯控制系统为例来展示状态模式的代码实现。电梯有几种不同的状态,比如静止、上升、下降等,每种状态下对于外部的请求(如按楼层按钮等)会有不同的响应行为。
#include <iostream>// 抽象状态类
class ElevatorState
{
public:virtual void open() = 0;virtual void close() = 0;virtual void run() = 0;virtual void stop() = 0;
};// 具体状态类:静止状态
class StoppedState : public ElevatorState
{
public:void open() override{std::cout << "电梯门打开,当前处于静止状态。" << std::endl;}void close() override {std::cout << "电梯门关闭,当前处于静止状态,准备运行。" << std::endl;}void run() override{std::cout << "电梯从静止状态开始运行。" << std::endl;}void stop() override {std::cout << "电梯已经处于静止状态,无需再次停止。" << std::endl;}
};// 具体状态类:上升状态
class RisingState : public ElevatorState
{
public:void open() override{std::cout << "电梯正在上升,不能开门。" << std::endl;}void close() override {std::cout << "电梯正在上升,门已关闭。" << std::endl;}void run() override{std::cout << "电梯继续上升。" << std::endl;}void stop() override{std::cout << "电梯上升过程中停止。" << std::endl;// 假设停止后切换到静止状态,可以在这里做相应状态切换逻辑,比如通知环境类切换状态}
};// 具体状态类:下降状态
class FallingState : public ElevatorState
{
public:void open() override {std::cout << "电梯正在下降,不能开门。" << std::endl;}void close() override{std::cout << "电梯正在下降,门已关闭。" << std::endl;}void run() override {std::cout << "电梯继续下降。" << std::endl;}void stop() override{std::cout << "电梯下降过程中停止。" << std::endl;// 同样,停止后可考虑切换状态逻辑}
};// 环境类:电梯
class Elevator
{
private:ElevatorState* currentState;
public:Elevator(){currentState = new StoppedState();}void setCurrentState(ElevatorState* state) {currentState = state;}void open(){currentState->open();}void close(){currentState->close();}void run() {currentState->run();}void stop() {currentState->stop();}
};int main()
{Elevator elevator;elevator.open();elevator.close();elevator.run();elevator.stop();RisingState risingState;elevator.setCurrentState(&risingState);elevator.open();elevator.close();elevator.run();elevator.stop();return 0;
}
C++示例代码2
#include<iostream>
#include<list>
#include<string>
using namespace std;
class Andy;//男主
class State//状态
{
public:string m_satedes;//描述当前的状态
public:virtual void Fbegin(){};//前期状态virtual void Fmid(){};//中期状态virtual void Fend(){};//末期状态virtual void CurrentState(Andy*p_andy){};//调用当前状态,统一的接口,然后各个状态子类去调用自己的函数
};
class Andy
{
private:State *m_state; //目前状态int m_years; //时间
public:Andy(State *state): m_state(state), m_years(0) {}~Andy() { delete m_state; }int GetYears() { return m_years; }void SetYears(int years) { m_years = years; }void SetState(State *state) { delete m_state; m_state = state; }void GetState() { m_state->CurrentState(this); }
};//服刑末期
class State_End:public State
{
public:State_End(){m_satedes = "挖洞逃出来了";}void FEnd(Andy *p_andy){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}void CurrentState(Andy *p_andy){FEnd(p_andy);}
};//服刑中期
class State_Mid:public State
{
public:State_Mid(){m_satedes = "服刑中期,每天帮监狱长做假账,顺便开始夜里挖洞";}void FMid(Andy *p_andy){if(p_andy->GetYears()<28){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}else{p_andy->SetState(new State_End());p_andy->GetState();}}void CurrentState(Andy *p_andy){FMid(p_andy);}
};//服刑前期
class State_begin:public State
{
public:State_begin(){m_satedes = "男主Andy刚进监狱,处处受人欺负";}void Fbegin(Andy *p_andy){if(p_andy->GetYears()<5){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}else{p_andy->SetState(new State_Mid());p_andy->GetState();}}void CurrentState(Andy *p_andy){Fbegin(p_andy);}
};int main()
{Andy *p = new Andy(new (State_begin));for(int i = 1; i < 40; ++i){p->SetYears(i);p->GetState();}delete p;return 0;
}
应用场景
游戏开发领域
角色状态管理:游戏中的角色通常有多种状态,如站立、行走、奔跑、跳跃、攻击、受伤、死亡等。使用状态模式可以为每个状态创建一个单独的类,在这些类中实现角色在该状态下的行为,如移动速度、动画播放、可执行的操作等。例如,当角色处于攻击状态时,其移动速度可能会降低,并且会播放攻击动画,同时能够触发攻击相关的逻辑,如伤害计算。
游戏场景状态切换:游戏场景也可以有不同的状态,像游戏的主菜单场景、游戏进行中的场景、暂停场景、游戏结束场景等。每个场景状态都有自己的一套显示内容、交互逻辑和更新机制。通过状态模式,可以方便地在不同场景状态之间切换,并且清晰地管理每个状态下的资源加载、渲染和事件处理。
图形用户界面(GUI)系统
窗口状态管理:一个窗口可能有多种状态,如最小化、最大化、正常、隐藏等。在不同状态下,窗口的显示方式、对用户操作的响应等都不同。利用状态模式,可以将每个状态对应的行为封装到相应的状态类中。例如,当窗口处于最小化状态时,它可能只在任务栏显示一个图标,并且点击图标时的操作(如恢复窗口大小)与窗口处于正常状态时的操作(如拖动边框改变大小)是不同的。
按钮状态控制:按钮有正常、鼠标悬停、按下等状态。在不同状态下,按钮的外观(如颜色、样式)和行为(如触发的事件)不同。状态模式可以让开发者轻松地定义每个状态下按钮的渲染逻辑和事件响应逻辑,使得按钮的状态管理更加清晰和易于维护。
工作流系统和业务流程管理
订单处理流程:在电商系统中,订单有多种状态,如已创建、已付款、已发货、已签收、已退款等。每个状态下的订单处理逻辑不同,例如在 “已付款” 状态下,系统会通知仓库准备发货;在 “已发货” 状态下,系统会提供物流信息查询等服务。通过状态模式,可以把订单在不同状态下的处理逻辑封装在对应的状态类中,方便业务流程的扩展和维护。
审批流程:企业中的审批流程也存在多种状态,如提交审批、部门主管审批中、高层领导审批中、审批通过、审批拒绝等。对于每个状态,都有相应的操作和流转规则,比如在 “部门主管审批中” 状态下,部门主管可以查看申请内容并进行审批操作,审批通过后状态会切换到 “高层领导审批中”。使用状态模式可以清晰地实现这种复杂审批流程的状态管理和操作逻辑。
网络协议和通信系统
TCP 连接状态管理:在 TCP 协议中,连接有多种状态,如 LISTEN(监听)、SYN - SENT(同步已发送)、SYN - RECEIVED(同步收到)、ESTABLISHED(已建立)、FIN - WAIT - 1(终止等待 1)等。在不同状态下,网络设备(如服务器和客户端)的行为和数据处理方式不同。状态模式可以用于实现 TCP 连接状态的管理,将每个状态对应的数据包处理、连接维护等操作封装在相应的状态类中,使得网络通信的状态处理更加清晰和可靠。
设备通信状态:在物联网系统中,设备之间的通信可能会出现多种状态,如连接正常、连接中断、数据传输中、等待响应等。对于每种状态,可以使用状态模式来定义设备在该状态下的通信行为,如重新连接策略、数据缓存和发送方式等,以确保设备之间的通信稳定和高效。
相关文章:
重拾设计模式--状态模式
文章目录 状态模式(State Pattern)概述状态模式UML图作用:状态模式的结构环境(Context)类:抽象状态(State)类:具体状态(Concrete State)类&#x…...
稀疏矩阵的存储与计算 gaxpy
1, gaxpy 数学公式 其中: , , 2, 具体实例 3,用稠密矩阵的方法 本节将用于验证第4节中的稀疏计算的结果 hello_gaxpy_dense.cpp #include <stdio.h> #include <stdlib.h>struct Matrix_SP {float* val; //…...
基于LabVIEW的USRP信道测量开发
随着无线通信技术的不断发展,基于软件无线电的设备(如USRP)在信道测量、无线通信测试等领域扮演着重要角色。通过LabVIEW与USRP的结合,开发者可以实现信号生成、接收及信道估计等功能。尽管LabVIEW提供了丰富的信号处理工具和图形…...
基于LSTM长短期记忆神经网络的多分类预测【MATLAB】
在深度学习中,长短期记忆网络(LSTM, Long Short-Term Memory)是一种强大的循环神经网络(RNN)变体,专门为解决序列数据中的长距离依赖问题而设计。LSTM因其强大的记忆能力,广泛应用于自然语言处理…...
物联网:全面概述、架构、应用、仿真工具、挑战和未来方向
中文论文标题:物联网:全面概述、架构、应用、仿真工具、挑战和未来方向 英文论文标题:Internet of Things: a comprehensive overview, architectures, applications, simulation tools, challenges and future directions 作者信息&#x…...
volatility2工具的使用vol2工具篇
vol2工具 命令格式:vol.py -f [image] --profile[profile] [plugin] 1、查看系统的操作版本,系统镜像信息 2.查看用户名密码信息,当前操作系统中的password hash,例如SAM文件内容 3.从注册表提取LSA密钥信息(已解密&…...
R 基础运算
R 基础运算 R 是一种广泛使用的统计编程语言,它提供了强大的数据操作和分析功能。基础运算在 R 中非常重要,因为它们是进行更复杂计算和数据分析的基础。本文将详细介绍 R 中的基础运算,包括算术运算、逻辑运算、向量化和矩阵运算。 一、算…...
javaScriptBOM
1.1、BOM概述 1.1.1、BOM简介 BOM(browser Object)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window。 BOM由一系列的对象构成,并且每个对象都提供了很多方法与属性 BOM缺乏标准…...
Godot RPG 游戏开发指南
Godot RPG 游戏开发指南 一、基础准备 1. 开发环境 下载并安装最新版 Godot 4.x选择使用 GDScript 或 C# 作为开发语言准备基础美术资源(角色、地图、道具等) 2. 项目结构 project/ ├── scenes/ # 场景文件 ├── scripts/ # 脚…...
目标检测数据集图片及标签同步旋转角度
前言 在深度学习领域,尤其是目标检测任务中,数据集的质量直接影响模型的性能。为了提升模型的鲁棒性和对各种场景的适应能力,数据增强技术被广泛应用于图像数据集处理。旋转角度是常见的数据增强方法,通过对图像及其对应的标签&am…...
2025前端面试热门题目——计算机网络篇
计算机网络篇——面试 1. 到底什么是 TCP 连接? TCP 连接的定义 TCP(传输控制协议)是一个面向连接的传输层协议。TCP 连接是通过 三次握手 确立的可靠数据通信链路,保证了在不可靠网络(如互联网)上的数据传输的准确…...
LEAST-TO-MOST PROMPTING ENABLES COMPLEX REASONING IN LARGE LANGUAGE MODELS---正文
题目 最少到最多的提示使大型语言模型能够进行复杂的推理 论文地址:https://arxiv.org/abs/2205.10625 摘要 思路链提示在各种自然语言推理任务中表现出色。然而,它在需要解决比提示中显示的示例更难的问题的任务上表现不佳。为了克服这种由易到难的概括…...
Java开发经验——日志治理经验
摘要 本文主要介绍了Java开发中的日志治理经验,包括系统异常日志、接口摘要日志、详细日志和业务摘要日志的定义和目的,以及错误码规范和异常处理规范。强调了日志治理的重要性和如何通过规范化错误码和日志格式来提高系统可观测性和问题排查效率。 1. …...
使用复数类在C#中轻松绘制曼德布洛集分形
示例在 C# 中绘制曼德布洛特集分形解释了如何通过迭代以下方程来绘制曼德布洛特集: 其中 Z(n) 和 C 是复数。程序迭代此方程,直到 Z(n) 的大小至少为 2 或程序执行最大迭代次数。 该示例在单独的变量中跟踪数字的实部和虚部。此示例使用Complex类来更轻松…...
VSCode 启用免费 Copilot
升级VSCode到 1.96版本,就可以使用每个月2000次免费额度了,按照工作日每天近80次免费额度,满足基本需求。前两天一直比较繁忙,今天周六有时间正好体验一下。 引导插件安装GitHub Copilot - Visual Studio Marketplace Extension f…...
常见问题整理
DevOps 和 CICD DevOps 全称Development & Operation 一种实现开发和运维一体化的协同模式,提供快速交付应用和服务的能力 用于协作:开发,部署,质量测试 整体生命周期工作内容,最终实现持续继承,持续部…...
使用Vue创建前后端分离项目的过程(前端部分)
前端使用Vue.js作为前端开发框架,使用Vue CLI3脚手架搭建项目,使用axios作为HTTP库与后端API交互,使用Vue-router实现前端路由的定义、跳转以及参数的传递等,使用vuex进行数据状态管理,后端使用Node.jsexpress…...
【Springboot知识】Redis基础-springboot集成redis相关配置
文章目录 1. 添加依赖2. 配置Redis连接3. 配置RedisTemplate(可选)4. 使用RedisTemplate或StringRedisTemplate5. 测试和验证 集群配置在application.properties中配置在application.yml中配置 主从配置1. 配置Redis服务器使用配置文件使用命令行 2. 配置…...
网络安全概论——身份认证
一、身份证明 身份证明可分为以下两大类 身份验证——“你是否是你所声称的你?”身份识别——“我是否知道你是谁?” 身份证明系统设计的三要素: 安全设备的系统强度用户的可接受性系统的成本 实现身份证明的基本途径 所知:个…...
OpenHarmony-4.HDI 框架
HDI 框架 1.HDI介绍 HDI(Hardware Device Interface,硬件设备接口)是HDF驱动框架为开发者提供的硬件规范化描述性接口,位于基础系统服务层和设备驱动层之间,是连通驱动程序和系统服务进行数据流通的桥梁,是…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
