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++设计模式——状态模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/123798.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ionic 7 版本发布 - 免费开源、超受欢迎的移动应用开发 UI 工具包,主题优雅且完美支持 Vue.js

Ionic 是一款优秀的移动 UI 框架,迭代也很快,现在也支持了 Vue,是时候向大家推荐用来开发 APP 了。 Ionic 全称是 Ionic Framework,是一个功能强大的开源 UI 工具库,用来帮助前端开发者构建跨平台的移动应用。 Ionic …

rhcsa目录练习

要求 在根下创建一个叫做test的目录,在test的目录下创建三个普通文件file1 file2 file3,给file1创建一个软链接aa,给file2创建两个硬链接,在test目录下创建一个ceshi的目录,在ceshi的目录下创建a1-a8,b1-b8,ac1-ad8的…

【前端框架】本文带你了解nvue

前言 各位公主给👸🏻,王子🤴🏻好,我是你们的Aic山鱼,专注于前端领域的垂直更新。我热衷于分享我的经验和知识,希望能够帮助更多的人在前端领域取得进步。作为一名前端开发人员&#…

Linux 系统调用IO口,利用光标偏移实现文件复制

用系统调用IO函数实现从一个文件读取最后2KB数据并复制到另一个文件中,源文件以只读方式打开,目标文件以只写的方式打开,若目标文件不存在,可以创建并设置初始值为0664,写出相应代码,要对出错情况有一定的处…

【C++】搜索二叉树

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、搜索二叉树概念二、搜索二叉树的操作1.插入2. 查找3. 中序遍历4. 删除 三、默认成员函数1.析构函数2.拷贝构造3. 赋值运算符重载 四、完整代码 一、搜索二叉树概…

SSM宾馆客房管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 宾馆客房管理系统是一套完善的信息系统,结合springboot框架和bootstrap完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代 码和数据库,系统…

任正非说:扩张必须踩在坚实的基础上,擅自扩张只能是自杀。

嗨,你好!这是华研荟【任正非说】系列的第23篇文章,让我们继续聆听任正非先生的真知灼见,学习华为的管理思想和管理理念。 一、要想赢,要么在剑法上高于人,要么在盾牌上坚于人。若果剑不如人,就要…

大厂面试题-JVM为什么使用元空间替换了永久代?

目录 面试解析 问题答案 面试解析 我们都知道Java8以及以后的版本中,JVM运行时数据区的结构都在慢慢调整和优化。但实际上这些变化,对于业务开发的小伙伴来说,没有任何影响。 因此我可以说,99%的人都回答不出这个问题。 但是…

一文读懂:到底什么是Chiplets?

50多年来,Intel和AMD一直是两家主流的处理器公司。 虽然两者都使用x86 ISA来设计新品,但是在过去十多年左右的时间里,两家cpu公司却走上了完全不同的道路。 大约在2000年代中期,随着推土机(Bulldozer)芯片推出,AMD在…

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧,非常容易上手: # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK:欢迎访问主页!"if __name__ "__main__"…

OpenCV学习笔记

OpenCV基础 threshold函数的使用 https://blog.csdn.net/u012566751/article/details/77046445 图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。在数字图像处理中,二值图像占有非常重要的地位&#xff0…

线扫相机DALSA--常见问题一:软件安装顺序

1.软件安装顺序 先安装:Sapera_LT_SDK,后安装Xtium-CL MX4驱动。 2.初次安装CamExpert,重启电脑后未找到相机 Settings(搜索协议)配置完毕后,需点击Detect Camera(一键查找相机)按钮,搜索相机。第一次查找相机耗时会略…

聚观早报 |蔚来推出婚车服务;长城汽车第三季度财报

【聚观365】10月30日消息 蔚来推出婚车服务 长城汽车第三季度财报 AI汽车机器人极越01上市 谷歌投资初创公司Anthropic 东方财富第三季度营收 蔚来推出婚车服务 据蔚来汽车官方消息,蔚来宣布推出“蔚来用户专享”的婚庆用车定制服务。 据悉,该服务…

Mac删除照片快捷键ctrl加什么 Mac电脑如何批量删除照片

Mac电脑是很多人喜欢使用的电脑,它有着优美的设计、高效的性能和丰富的功能。如果你的Mac电脑上存储了很多不需要的照片,那么你可能会想要删除它们,以节省空间和提高速度。那么,Mac删除照片快捷键ctrl加什么呢?Mac电脑…

微信小程序实现微信登录(Java后台)

这两天在自己的小项目中加入了微信小程序的很多功能,今天来说一下关于微信授权登录的部分。 需要的材料 1:一个可以测试的微信小程序 2:此微信小程序的APPID和APPscret 流程 微信用户对应一个小程序都有一个唯一的openid,微信…

【文末送书】AI时代数据的重要性

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…

分享个自己写的小程序解包工具

闲聊 前几天在吾爱破解上看到一个小程序逆向的帖子:windows下通杀wx小程序云函数实战 ,想着自己也学习一下怎么逆向小程序,一搜 unveilr 仓库没了,看评论好像开始收费了。 我就用aardio写了一个解密和解包工具,这里免…

Kotlin协程核心理解

一、协程是什么? 1.1 基本概念的理解 我们知道JVM中的线程的实现是依赖其运行的操作系统决定的,JVM只是在上层进行了API的封装,包含常见的有线程的启动方法,状态的管理,比如:Java中抽象出了6种状态&#x…

深入理解指针3

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1. 字符指针变量 2. 数组指针变量 2.1 数组指针变量是什么? 2.2 数组指针变量怎么初始化 3. 二维数组传参的本质 4. 函数指针变量 4.1 函数指针变量的创…

并发安全问题之超卖问题

并发安全问题之超卖问题 乐观锁总结: 优点:不加锁性能好。 缺点:同时请求成功率低(即只要发现数据变了就放弃了)。 乐观锁思想的具体体现:一共两步,第一步,先查询状态。第二步&…