实战设计模式之状态模式
概述
作为一种行为设计模式,状态模式允许对象在其内部状态改变时,改变其行为。这种模式通过将状态逻辑从对象中分离出来,并封装到独立的状态类中来实现。每个状态类代表一种特定的状态,拥有自己的一套行为方法。当对象的状态发生变化时,它会切换到另一个状态类,从而改变其行为。
交通信号灯是现实生活中运用状态模式的一个典型例子:红绿黄三种颜色代表了三种不同的状态,每个状态都有特定的行为和持续时间。比如:红色灯表示停止,绿色灯表示通行,黄色灯则提醒驾驶员准备停车。状态模式可以帮助管理系统中信号灯的状态转换逻辑,确保每次只有一种状态是激活的,并且根据预定的时间间隔自动切换到下一个状态。
基本原理
状态模式的核心思想在于:将对象的行为与其状态分离,使得状态的变化能够自然地反映在对象的行为上。这种方法不仅提高了代码的可维护性和扩展性,还促进了面向对象设计原则的应用。无论是开发复杂的游戏系统、构建响应式的用户界面、还是处理复杂的业务流程,状态模式都提供了一种有效的方式来管理对象的状态变化及其相应的行为调整。在状态模式中,一个对象看起来像是改变了它的类一样,因为它根据内部状态的不同而表现出不同的行为。这对于那些需要根据不同状态执行不同操作的应用场景非常有用。
状态模式主要由以下三个核心组件构成。
1、上下文。上下文定义了客户端感兴趣的接口,并维护当前状态的引用。它通过委托给具体的状态对象来执行请求,从而实现状态转换和行为变化。
2、状态接口。定义了一个接口或抽象类,表示所有具体状态的共同行为。该接口通常包含了一些声明的方法,这些方法代表了状态可以执行的操作。
3、具体状态。实现了状态接口的具体类,每种具体状态都负责定义在该状态下应执行的行为。具体状态可以根据需要决定是否要进行状态转换,即在特定条件下改变上下文的当前状态。
基于上面的核心组件,状态模式的实现主要有以下四个步骤。
1、定义状态接口。创建一个接口或抽象类,定义所有具体状态都应该实现的方法。这些方法通常是与上下文相关的操作,比如:执行某个动作,或触发状态转换。
2、创建具体状态类。针对每一个可能的状态,创建具体的实现类。在具体状态类中,我们会实现对应的方法,这些方法定义了在该状态下应该执行的行为,以及如何进行状态转换。
3、实现上下文类。创建上下文类,它持有一个指向当前状态的引用,并提供方法来设置当前状态。上下文类还提供了对外的接口,用于接收来自客户端的请求,并将这些请求委托给当前状态对象来处理。
4、集成状态和上下文。在上下文类中初始化默认状态,并根据业务逻辑的变化动态地切换状态。当上下文接收到请求时,它会调用当前状态对象的相关方法,由具体的状态对象来决定如何响应请求,并可能触发状态转换。
实战代码
在下面的实战代码中,我们使用状态模式模拟了交通信号灯的实现。
首先,我们定义了抽象状态接口CTrafficLightState。它声明了一个纯虚函数Handle,该函数接受一个CTrafficLight类型的引用作为参数。这个接口为所有具体状态提供了统一的行为规范,确保每个状态都能正确处理请求并可能触发状态转换。
接着,我们实现了上下文类CTrafficLight,它维护了一个指向当前状态的指针m_pState。在构造函数中,初始化了初始状态,并在析构函数中删除了该状态对象。SetState方法用于改变当前状态,在设置新状态之前会先释放旧的状态对象。Request方法则是触发当前状态处理逻辑的入口点,如果当前状态有效,则调用其Handle方法。
然后,我们定义了三个具体状态类CRedLightState、CGreenLightState和CYellowLightState,分别表示红灯、绿灯和黄灯三种状态。每个具体状态类都重写了Handle方法,定义了在该状态下应执行的操作,以及如何进行状态转换。比如:当红灯状态被处理时,输出提示信息,并在模拟的时间延迟后切换到绿灯状态。
最后,在main函数中,我们创建了一个初始状态为红灯的CTrafficLight实例,并通过循环调用Request方法模拟了几个周期的交通信号灯变化过程。每次调用Request都会导致当前状态对象处理请求,并根据内部逻辑决定是否需要切换到下一个状态。
#include <iostream>using namespace std;// 抽象状态接口
class CTrafficLightState
{
public:virtual ~CTrafficLightState() = default;virtual void Handle(class CTrafficLight& trafficLight) = 0;
};// 上下文:交通信号灯
class CTrafficLight
{
public:CTrafficLight(CTrafficLightState* pInitial) : m_pState(pInitial) {}~CTrafficLight(){delete m_pState;}void SetState(CTrafficLightState* pNewState){delete m_pState;m_pState = pNewState;cout << "Traffic Light State Changed" << endl;}void Request(){if (m_pState != NULL){m_pState->Handle(*this);}}private:CTrafficLightState* m_pState;
};// 具体状态:红灯
class CRedLightState : public CTrafficLightState
{
public:void Handle(CTrafficLight &TrafficLight) override;
};// 具体状态:绿灯
class CGreenLightState : public CTrafficLightState
{
public:void Handle(CTrafficLight &TrafficLight) override;
};// 具体状态:黄灯
class CYellowLightState : public CTrafficLightState
{
public:void Handle(CTrafficLight &TrafficLight) override;
};void CRedLightState::Handle(CTrafficLight& TrafficLight)
{cout << "Red Light: Stop" << endl;// 假设红灯持续5秒后,切换到绿灯TrafficLight.SetState(new CGreenLightState());
}void CGreenLightState::Handle(CTrafficLight& TrafficLight)
{cout << "Green Light: Go" << endl;// 假设绿灯持续10秒后,切换到黄灯TrafficLight.SetState(new CYellowLightState());
}void CYellowLightState::Handle(CTrafficLight& TrafficLight)
{cout << "Yellow Light: Prepare To Stop" << endl;// 假设黄灯持续3秒后,切换到红灯TrafficLight.SetState(new CRedLightState());
}int main()
{// 初始化交通信号灯为红灯状态CTrafficLight TrafficLight(new CRedLightState());// 模拟交通信号灯的变化for (int i = 0; i < 3; ++i){// 处理当前状态,并切换到下一个状态TrafficLight.Request(); }return 0;
}
总结
状态模式通过将状态逻辑分离到不同的类中,减少了大量使用if else或switch case语句的需求,使得代码更加简洁和清晰。由于每个状态都被封装在一个独立的类中,这使得添加新状态或修改现有状态变得简单,不会影响到其他状态或业务逻辑。对于那些需要处理多个状态及其转换的应用场景,状态模式提供了一种结构化的方法来组织状态逻辑,使得系统更易于理解和维护。
尽管状态模式简化了单个状态下的行为逻辑,但在某些情况下,状态之间的转换逻辑可能会变得相当复杂,尤其是在状态之间存在多种可能的转换路径时。管理这些转换逻辑,可能需要额外的努力和仔细的设计。另外,频繁的状态切换可能会引入一定的性能开销,因为每次状态切换都需要创建新的状态对象或更新当前状态对象的引用。
相关文章:

实战设计模式之状态模式
概述 作为一种行为设计模式,状态模式允许对象在其内部状态改变时,改变其行为。这种模式通过将状态逻辑从对象中分离出来,并封装到独立的状态类中来实现。每个状态类代表一种特定的状态,拥有自己的一套行为方法。当对象的状态发生变…...
人工智能、机器学习与深度学习:概念解析与内在联系
人工智能、机器学习与深度学习:概念解析与内在联系 一、人工智能(Artificial Intelligence, AI) (一)人工智能的定义 人工智能的定义随着技术发展不断演变。从广义上讲,人工智能是指通过计算机技术实现的…...

什么是着色器 Shader
本人就是图形学结课了,对 OpenGL着色器还有很多疑问嘿嘿 文章目录 为什么要有着色器vshaderfshader 本文围绕 vshader 和 fshader 代码示例讲解。 (着色器代码取自本人简单OpenGL项目 https://github.com/DBWGLX/-OpenGL-3D-Lighting-and-Shadow-Modeli…...

Redis的主从架构
主从模式 全量同步 首先主从同步过程第一步 会先比较replication id 判断是否是第一次同步假设为第一次同步 那么就会 启动bgsave异步生成RDB 同时fork子进程记录生成期间的新数据发送RDB给从节点 清空本地数据写入RDB 增量同步 对比ReplicationID不同因此选择增量同步在Rep…...

博客系统功能测试
博客系统网址:http://8.137.19.140:9090/blog_list.html 主要测试内容 功能测试、界面测试、性能测试、易用性测试、安全测试、兼容性测试、弱网测试、安装卸载测试、压力测试… 测试方法及目的 利用selenium和python编写测试脚本,对博客系统进行的相关…...

【深度学习新浪潮】什么是多模态大模型?
多模态大模型是人工智能领域的前沿技术方向,它融合了多种数据模态(如文本、图像、语音、视频、传感器数据等),并通过大规模参数模型实现跨模态的联合理解与生成。简单来说,这类模型就像人类一样,能同时“看”“听”“读”“说”,并将不同信息关联起来,完成复杂任务。 …...

机器学习前言2
1.机器学习 2.机器学习模型 3.模型评价方法 4.如何选择合适的模型 介绍 机器学习(Machine Learning, ML)是人工智能(AI)的核心分支,致力于通过数据和算法让计算机系统自动“学习”并改进性能,而无需显式编…...

【成品设计】基于Arduino的自动化农业灌溉系统
《基于STM32的单相瞬时值反馈逆变器》 硬件设计: ESP-C3最小系统板:主控芯片,内部集成wifi。土壤湿度传感器:采集土壤湿度。温度传感器:采集土壤温度。水泵模块:水泵继电器软管。按键3个:参数…...

前端页面 JavaScript数据交互
前言:学习JavaScript为前端设置动态效果,实现交互。JavaScript是一种广泛应用于网页开发的脚本语言,它能够使网页变得更加动态和交互性。作为一种客户端脚本语言,JavaScript可以被嵌入到HTML中,并且可以被所有现代的网…...

esp32课设记录(三)mqtt通信记录 附mqtt介绍
目录 安装mqttx(云端部署) 安装mosquitto(本地部署) 编程,连接wifi 编程,连接mqtt,实现数据接收 实际效果展示: 附录:mqtt介绍 工作流程简述: 工作流…...

string类(详解)
【本节目标】 1. 为什么要学习string类 2. 标准库中的string类 3. string类的模拟实现 4. 扩展阅读 1. 为什么学习string类? 1.1 C语言中的字符串 C 语言中,字符串是以 \0 结尾的一些字符的集合,为了操作方便, C 标准库中提供…...

MATLAB | R2025a 更新了哪些有趣的东西?
千呼万唤始出来,MATLAB R2025A 来见面,这次更新比往常晚了两个月,让我们看看更了哪些好玩的新东西叭:首先下载更新启动一气呵成,映入眼帘的是: 1 基本界面 基本界面变得和 MATLAB 网页版一模一样了&#…...

前缀和——和为K的子数组
作者感觉本题稍稍有点难度,看了题解也思考了有一会TWT 显然,暴力我们是不可取的,但这里我们可以采取一种新的遍历数组形式,从后向前,也就是以i位置为结尾的所有子数组,这个子数组只统计i位置之前的。 然后…...
React 第四十二节 Router 中useLoaderData的用途详解
一、前言 useLoaderData,用于在组件中获取路由预加载的数据。它通常与路由配置中的 loader 函数配合使用,用于在页面渲染前异步获取数据(如 API 请求),并将数据直接注入组件,从而简化数据流管理。 二、us…...
千问大模型部署
参考链接:https://zhuanlan.zhihu.com/p/19698254692 tips:部署比较简单,除去上述教程中的步骤外,该文章主要是想记录过程中遇到的问题。因为我是双平台(arm/x86)部署,x86在python3.xx版本成功…...

深入理解 ZAB:ZooKeeper 原子广播协议的工作原理
目录 ZAB 协议:ZooKeeper 如何做到高可用和强一致?🔒ZAB 协议的核心目标 🎯ZAB 协议的关键概念 💡ZAB 协议的运行阶段 🎬阶段一:Leader 选举 (Leader Election) 🗳️阶段二ÿ…...
GO语言语法---if语句
文章目录 1. 基本语法1.1 单分支1.2 双分支1.3 多分支 2. Go特有的if语句特性2.1 条件前可以包含初始化语句2.2 条件表达式不需要括号2.3 必须使用大括号2.4 判断语句所在行数控制 Go语言的if语句用于条件判断,与其他C风格语言类似,但有一些独特的语法特…...
Unix Bourne Shell
本文来源 : 腾讯元宝 Unix Bourne Shell(简称sh)是Unix系统中最经典的命令行解释器(shell),由Stephen Bourne于1977年在贝尔实验室开发,并成为后续众多shell(如bash、ksh等ÿ…...

GraphPad Prism项目的管理
《2025新书现货 GraphPad Prism图表可视化与统计数据分析(视频教学版)雍杨 康巧昆 清华大学出版社教材书籍 9787302686460 GraphPadPrism图表可视化 无规格》【摘要 书评 试读】- 京东图书 GraphPad Prism统计数据分析_夏天又到了的博客-CSDN博客 项目…...

驱动-Linux定时-timer_list
了解内核定时相关基础知识 文章目录 简要介绍timer_list 特点API 函数实验测试程序 - timer_mod.c编译文件-Makefile实验验证 注意事项总结 简要介绍 硬件为内核提供了一个系统定时器来计算流逝的时间(即基于未来时间点的计时方式, 以当前时刻为计时开始…...

STM32F103_LL库+寄存器学习笔记22 - 基础定时器TIM实现1ms周期回调
导言 如上所示,STM32F103有两个基本定时器TIM6与TIM7,所谓「基本定时器」,即功能最简单的定时器。 项目地址: github: LL库: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library22_Basic_Timer寄存器方…...

5个yyds的.Net商城开源项目
今天一起来盘点下5个商城开源项目。 1、支持多语言、多商店的商城,.Net7 EF7领域驱动设计架构(Smartstore) 项目简介 Smartstore 支持桌面和移动平台、多语言、多商店、多货币的商城,并支持SEO优化,支持无限数量的…...
C++:与7无关的数
【描述】 一个正整数,如果它能被7整除,或者它的十进制表示法中某一位上的数字为7,则称其为与7相关的数.现求所有小于等于n(n < 100)的与7无关的正整数的平方和. 【输入】 输入为一行,正整数n(n < 100) 【输出】 输出一行,包含一个整数,即小于等于n…...

[项目深挖]仿muduo库的并发服务器的解析与优化方案
标题:[项目深挖]仿muduo库的并发服务器的优化方案 水墨不写bug 文章目录 一、buffer 模块(1)线性缓冲区直接扩容---->环形缓冲区定时扩容(只会扩容一次)(2)使用双缓冲(Double Buf…...
c语言与python的异同之处
一、基本语法对比 1. 注释 // C语言单行注释/* C语言多行注释 */# Python单行注释 Python多行注释 2. 变量声明 // C语言int number 10;float price 3.14;char letter A;# Pythonnumber 10price 3.14letter A 3. 条件语句 // C语言if (condition) {// 代码块} else …...

国标GB28181视频平台EasyGBS校园监控方案:多场景应用筑牢安全防线,提升管理效能
一、方案背景 随着校园规模不断扩大,传统监控系统因设备协议不兼容、数据分散管理,导致各系统之间相互独立、数据无法互通共享。在校园安全防范、教学管理以及应急响应过程中,这种割裂状态严重影响工作效率。国标GB28181软件EasyGBS视频云…...
【视频】解决FFmpeg将RTSP转RTMP流时,出现的卡死、出错等问题
【视频】郭老二博文之:图像视频汇总 1、简述 如果不修改图像内容,可以使用FFmpeg命令来将RTSP转RTMP流。 SRS视频服务器就是这么干的,它没有使用FFmpeg接口,而是直接使用FFmpeg命令来转流。 但是在使用中,约到了一些问题,比如转流时卡死、转流出错等等,下面描述怎么解…...

SHIMADZU岛津 R300RC300 Operation Manual
SHIMADZU岛津 R300RC300 Operation Manual...

使用 Docker 部署 React + Nginx 应用教程
目录 1. 创建react项目结构2. 创建 .dockerignore3. 创建 Dockerfile4. 创建 nginx.conf5. 构建和运行6. 常用命令 1. 创建react项目结构 2. 创建 .dockerignore # 依赖目录 node_modules npm-debug.log# 构建输出 dist build# 开发环境文件 .git .gitignore .env .env.local …...
我创建了MariaDb数据库,但其他电脑访问不到?
今天上班有个有趣的事情,我在windowsserver创建了数据库,但有趣的是我安装完,服务器外的电脑无法访问。 我在windows server 服务器 搭建了mariadb 172.18.0.192 3306端口 账号密码为 root 123456.但是我在外部电脑中访问不了,就…...