当前位置: 首页 > news >正文

C++设计模式_18_State 状态模式

State和Memento被归为“状态变化”模式。

文章目录

  • 1. “状态变化”模式
    • 1.1 典型模式
  • 2. 动机 (Motivation)
  • 3. 代码演示State 状态模式
    • 3.1 常规方式
    • 3.2 State 状态模式
  • 4. 模式定义
  • 5. 结构( Structure )
  • 6. 要点总结
  • 7. 其他参考

1. “状态变化”模式

  • 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。

1.1 典型模式

  • State
  • Memento

2. 动机 (Motivation)

  • 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
  • 如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?

3. 代码演示State 状态模式

3.1 常规方式

假设有一个网络的应用,会根据网路状态做一些调整,比如网络有打开,关闭,连接三种状态

enum NetworkState
{Network_Open,Network_Close,Network_Connect,
};

下面的类为网络的应用,NetworkState state;是网络的状态,Operation1()在打开状态Network_Open下具有不同的行为,执行完后,最后状态变为close…;Operation2()则是另外的行为,在打开状态Network_Open下具有不同的行为,执行完后,最后状态变为Network_Connect…。

整体代码如下:

class NetworkProcessor{NetworkState state;public:void Operation1(){if (state == Network_Open){//**********state = Network_Close;}else if (state == Network_Close){//..........state = Network_Connect;}else if (state == Network_Connect){//$$$$$$$$$$state = Network_Open;}}public void Operation2(){if (state == Network_Open){//**********state = Network_Connect;}else if (state == Network_Close){//.....state = Network_Open;}else if (state == Network_Connect){//$$$$$$$$$$state = Network_Close;}}public void Operation3(){}
};

动机 (Motivation)中提到的“对象的状态如果改变,其行为也会随之而发生变化”,Operation1()中的if…else已经很清楚的表明了Operation1根据状态不同而行为不同。

上面的写法会有什么问题?看到这段代码,大家可能会有似曾相识的感觉,根据上面代码来看和Strategy模式时碰到的问题是一样的,代码中出现了很多的if…else,而且if…else是有关业务状态。Strategy模式告诉我们对于这种情况我们应该问一个为什么?

以下的枚举类型,以后会不会有其他的类型出现?如果添加了一种新的状态,假设增加了Network_Wait,之前的代码如何更改?

enum NetworkState
{Network_Open,Network_Close,Network_Connect,Network_Wait,
};

增加了新的状态之后,在不同的operation中需要增加新的if…else。这样的做法显然是违背了开闭原则。即需求的变更导致需要在代码中不断地去改这些地方。

3.2 State 状态模式

上面的做法肯定是不好的,好的做法是参考Strategy模式的经验,先提抽象基类,把枚举类型转换为一个抽象基类

class NetworkState{public:NetworkState* pNext;virtual void Operation1()=0;virtual void Operation2()=0;virtual void Operation3()=0;virtual ~NetworkState(){}
};

所有跟状态有关的操作Operation1()等全部变为状态对象的行为,并利用多态的虚函数来进行表达,并塞入一个NetworkState* pNext;状态对象的指针。

创建OpenState,其中使用了前面所讲的单例模式,在状态中倾向使用单例模式,因为状态的对象没必要有多个。此处Operation1()里面省略部分的内容是与上面常规的方式是有差别的,核心逻辑肯定是类似的,经过Operation1()之后就变为close状态,此时pNext = CloseState::getInstance()换的是一个对象,而不是一个枚举。总之是将状态相关的操作,全部编码到一个状态对象中。

如果是在open状态,而且执行的是Operation1()的时候会执行怎样的行为,执行完之后下一个状态会切换为哪个状态。

class OpenState :public NetworkState{static NetworkState* m_instance;
public:static NetworkState* getInstance(){if (m_instance == nullptr) {m_instance = new OpenState();}return m_instance;}void Operation1(){//**********pNext = CloseState::getInstance();}void Operation2(){//..........pNext = ConnectState::getInstance();}void Operation3(){//$$$$$$$$$$pNext = OpenState::getInstance();}};

以此类推,得到了close状态等,需要做状态对象。

整个网络应用就改写为状态对象,而不是枚举字段,这种方式与strategy处理的手法是异曲同工。Operation1()中先做收集参数工作,调用pState->Operation1();(虚函数的本质是运行时的if…else,在运行时会判断pState的指针如果指向的是open状态,就会调用open状态的operation1;如果指向的是不同状态就会执行对应状态的operation1),执行完之后就让其等于下一个状态 pState = pState->pNext;,下一个状态是Operation1()内部pNext = ConnectState::getInstance();更改的状态,我本身不用管。

class NetworkProcessor{NetworkState* pState;public://构造器中初始化pStateNetworkProcessor(NetworkState* pState){this->pState = pState;}void Operation1(){//...pState->Operation1();pState = pState->pNext;//...}void Operation2(){//...pState->Operation2();pState = pState->pNext;//...}void Operation3(){//...pState->Operation3();pState = pState->pNext;//...}};

上面这样做的好处是与Strategy异曲同工。当状态增加的时候,假如增加一个WaitState,仍然是像上面操作,写WaitState里的operation,而NetworkProcessor里是不用改变的,NetworkProcessor不关心其是什么状态,只关心状态上的行为是对的,状态行为里去自己去管理状态转换关系,是一种扩展的方法。

下为整体代码:

class NetworkState{public:NetworkState* pNext;virtual void Operation1()=0;virtual void Operation2()=0;virtual void Operation3()=0;virtual ~NetworkState(){}
};class OpenState :public NetworkState{static NetworkState* m_instance;
public:static NetworkState* getInstance(){if (m_instance == nullptr) {m_instance = new OpenState();}return m_instance;}void Operation1(){//**********pNext = CloseState::getInstance();}void Operation2(){//..........pNext = ConnectState::getInstance();}void Operation3(){//$$$$$$$$$$pNext = OpenState::getInstance();}};class CloseState:public NetworkState{ }
//...//扩展
class WaitState:public NetworkState{ }class NetworkProcessor{NetworkState* pState;public:NetworkProcessor(NetworkState* pState){this->pState = pState;}void Operation1(){//...pState->Operation1();pState = pState->pNext;//...}void Operation2(){//...pState->Operation2();pState = pState->pNext;//...}void Operation3(){//...pState->Operation3();pState = pState->pNext;//...}};

4. 模式定义

允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

----《设计模式》GoF

5. 结构( Structure )

在这里插入图片描述

上图是《设计模式》GoF中定义的State 状态模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。

在这里插入图片描述

State 状态模式和Strategy非常像,State中放的是一个行为,一般是多个行为,一个行为时和Strategy没什么两样的。

6. 要点总结

  • State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。

  • 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的–即要么彻底转换过莱,要么不转换

openstate值关心三个操作之后的下一个状态是什么,不需要太多的想更多耦合的情况。相比最初代码实现解耦。

  • 如果state对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销

State 状态模式讲下来和Strategy非常像,当最后往回看的时候,你会发现很多模式越来越像,相同点越来越多,学习模式最后会发现,这些模式之间差别会很小,会忘掉具体的模式名称。不一定非要纠结模式的名称,只是松耦合设计原则的演化。你可以将State 状态模式和Strategy看做同一个模式,出现的问题是If…else,枚举,怎么转,利用多态方式实现运行时的改变。掌握了这些,具体是什么模式就没那么重要了。

7. 其他参考

C++设计模式——状态模式

相关文章:

C++设计模式_18_State 状态模式

State和Memento被归为“状态变化”模式。 文章目录 1. “状态变化”模式1.1 典型模式 2. 动机 (Motivation)3. 代码演示State 状态模式3.1 常规方式3.2 State 状态模式 4. 模式定义5. 结构( Structure )6. 要点总结7. 其他参考 1. “状态变化”模式 在组件构建过程中&#xf…...

详解final, abstract, interface关键字

一.final关键字 1.final关键字介绍 ——final关键字可以去修饰类、方法、属性和局部变量 2.final关键字的作用 1)final修饰类,这个类不能被其他类继承 2)final修饰方法,方法不能被重写 3)final修饰属性,属…...

统计特殊四元组

题记&#xff1a; 给你一个 下标从 0 开始 的整数数组 nums &#xff0c;返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 &#xff1a; nums[a] nums[b] nums[c] nums[d] &#xff0c;且a < b < c < d 示例 1&#xff1a; 输入&#xff1a; nums [1,2,3…...

腾讯云轻量应用服务器“镜像”怎么选择合适?

腾讯云轻量应用服务器镜像怎么选择&#xff1f;如果是用来搭建网站可以选择宝塔Linux面板腾讯云专享版&#xff0c;镜像系统根据实际使用来选择&#xff0c;腾讯云百科txybk.com来详细说下腾讯云轻量应用服务器镜像的选择方法&#xff1a; 腾讯云轻量应用服务器镜像选择 轻量…...

Ruby模块和程序组织

和类一样&#xff0c;模块是一组方法和常量的集合。 和类不同&#xff0c;模块没有实例&#xff0c;取而代之的是可以将特殊模块的功能添加到一个类或者指定对象之中。 Class类是Module类的一个子类&#xff0c;因此每一个类对象也是一个模块对象 一、模块创建和基础应用 编写…...

14、SpringCloud -- WebSocket 实时通知用户

目录 实时通知用户需求:代码:前端:后端:WebSocket创建 websocket-server 服务添加依赖:配置 yml 和 启动类:前端:后端代码:注意:测试:总结:实时通知用户 需求: 用户订单秒杀成功之后,对用户进行秒杀成功通知。 弹出个提示框来提示。 代码: 前端:...

智能井盖传感器推荐,万宾科技助力城市信息化建设

随着科技产品更新换代进程加快&#xff0c;人工智能在人们日常生活之中逐渐普及开来&#xff0c;深入人们生活的方方面面&#xff0c;影响城市基础设施建设工程。例如在大街小巷之中的井盖作为城市基础建设的一个重要部分&#xff0c;一旦出现松动倾斜或凸起等异常问题&#xf…...

3D模型格式转换工具HOOPS Exchange对工业级3D产品HOOPS的支持与应用

一、概述 HOOPS Exchange是一套高性能模型转换软件库&#xff0c;可以给软件提供强大的模型的导入和导出功能&#xff0c;我们可以将其单独作为转换工具使用&#xff0c;也可以将其集成到自己的软件中。 同样&#xff0c;HOOPS 的其它产品&#xff0c;也离不开HOOPS Exchange…...

table 表体滚动, 表头、表尾固定

在开发报表中&#xff0c;如果报表数据行过多页面无法全部显示&#xff0c;或者内容溢出div&#xff0c;需要把表头和表尾固定表体滚动这样就可以在页面上全部显示&#xff0c;并且不会溢出div 效果&#xff1a;最终实现效果 代码&#xff1a;<!DOCTYPE html> <html&g…...

第57篇-某钩招聘网站加密参数分析【2023-10-31】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析1.X-S-HEADER参数2.请求参数data3.响应机密值data一、前言 网址: aHR0cHM6Ly93d3cubGFnb3UuY29t…...

C语言数据结构之数据结构入门

目录 数据结构介绍 数据结构发展史 何为算法 数据结构基础 基本概念和术语 四大逻辑结构&#xff08;Logic Structure&#xff09; 数据类型 理解复杂度概念 时间空间复杂度定义 度量时间复杂度的方法 程序运行时的内存与地址 编程预备 数据结构介绍 数据结构发展…...

如何知道服务器的某个端口是否打开

1、telnet 命令&#xff1a;telnet ip port&#xff0c;port即端口&#xff0c;我们一般最常见的命令就是telnet&#xff0c;但是telnet使用的是tcp协议&#xff0c;换句话说telnet只能检测tcp的这个端口打开了没 若是端口打开&#xff0c;会出现下列信息 失败的是这个 如…...

【ICCV‘23】One-shot Implicit Animatable Avatars with Model-based Priors

文章目录 前置知识 前置知识 1&#xff09;SMPL模型 \quad SMPL这类方法只建模穿很少衣服的人体&#xff08;裸体模型&#xff09;&#xff0c;它只能刻画裸体角色的动画&#xff0c;并不能刻画穿衣服的人体的动画 2&#xff09;data-efficient \quad 这个词推荐用&#xff…...

关于息肉检测和识别项目的总结

前言 整体的思路&#xff1a;首先息肉数据集分为三类&#xff1a; 1.正常细胞 2. 增生性息肉 3. 肿瘤要想完成这个任务&#xff0c;首先重中之重是分割任务&#xff0c;分割结果的好坏&#xff0c; 当分割结果达到一定的准确度后&#xff0c;开始对分割后的结果进行下游分类…...

Jetson Xavier NX FFmpeg支持硬件编解码

最近在用Jetson Xavier NX板子做视频处理,但是CPU进行视频编解码,效率比较地下。 于是便考虑用硬解码来对视频进行处理。 通过jtop查看,发现板子是支持 NVENC硬件编解码的。 1、下载源码 因为需要对ffmpeg进行打补丁修改,因此需要编译两份源码 1.1、编译jetson-ffmpeg …...

518抽奖软件,为什么说比别的抽奖软件更美观精美?

518抽奖软件简介 518抽奖软件&#xff0c;518我要发&#xff0c;超好用的年会抽奖软件&#xff0c;简约设计风格。 包含文字号码抽奖、照片抽奖两种模式&#xff0c;支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 精致美观功能 字体平滑无锯齿图片放大后清晰…...

React的组件学习

React的组件学习 参考资料&#xff1a;https://zh-hans.react.dev/learn/your-first-component 一、定义组件 export default function Profile() {return (<imgsrc"https://i.imgur.com/MK3eW3Am.jpg"alt"Katherine Johnson"/>) }以下是构建组件…...

uni-app配置微信开发者工具

一、配置微信开发者工具路径 工具->设置->运行配置->小程序运行配置->微信开发者工具路径 二、微信开发者工具开启服务端口...

肺癌不再是老年病:33岁作家的离世引发关注,有这些情况的请注意

近期&#xff0c;90后网络小说家七月新番和26岁男艺人蒋某某因肺癌去世&#xff0c;引发关注。他们都没有吸烟习惯&#xff0c;那么他们为什么会得肺癌呢&#xff1f;浙大二院呼吸内科副主任医师兰芬说&#xff0c;现在年轻人熬夜、加班导致身体过劳&#xff0c;在劳累情况下身…...

【兔子王赠书第4期】用ChatGPT轻松玩转机器学习与深度学习

文章目录 前言机器学习深度学习ChatGPT推荐图书粉丝福利尾声 前言 兔子王免费赠书第4期来啦&#xff0c;突破传统学习束缚&#xff0c;借助ChatGPT的神奇力量&#xff0c;解锁AI无限可能&#xff01; 机器学习 机器学习是人工智能领域的一个重要分支&#xff0c;它的目的是让…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...