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

news2024/12/23 5:16:30

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:
    
    //构造器中初始化pState
    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;
        //...
    }

};

上面这样做的好处是与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.coloradmin.cn/o/1153284.html

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

相关文章

Apache ActiveMQ (版本 < 5.18.3) (CNVD-2023-69477)RCE修复方案/缓解方案

一、漏洞描述 Apache ActiveMQ 是美国阿帕奇(Apache)基金会的一套开源的消息中间件,它支持 Java 消息服务、集群、Spring Framework 等。 二、漏洞成因 ActiveMQ 默认开放了 61616 端口用于接收 OpenWire 协议消息,由于针对异常…

sqlite3 关系型数据库语言 SQL 语言

SQL(Structured Query Language)语言是一种结构化查询语言,是一个通用的,功能强大的关系型数据库操作语言. 包含 6 个部分: 1.数据查询语言(DQL:Data Query Language) 从数据库的二维表格中查询数据,保留字 SELECT 是 DQL 中用的最多的语句 2.数据操作语言(DML) 最主要的关…

视频下载工具Downie4 mac中文支持格式

Downie4 mac是一款视频下载工具。它支持下载各种视频网站上的视频,并且具有快速、稳定、易于使用的特点。 Downie支持下载各种视频网站上的视频,包括YouTube、Vimeo、Netflix、Hulu、Amazon等等。它具有快速、稳定的下载速度,可以帮助用户轻松…

HDFS架构介绍

数新网络_让每个人享受数据的价值浙江数新网络有限公司是一家开源开放、专注于云数据智能操作系统和数据价值流通的服务商。公司自主研发的DataCyber云数据智能操作系统,主要包括数据平台CyberData、人工智能平台CyberAI、数据智能引擎CyberEngine、数据安全平台Cyb…

手机app爬虫配置 (苹果手机)

近期在做某个项目,涉及到需要对手机app的进行数据爬取。在上一篇博文中,讲述了以模拟机为例的配置操作流程,这里将以苹果手机为例进行描述。 下面将讲述具体配置步骤 1、安装 抓包软件 fiddler (Fiddler | Web Debugging Proxy and Troubleshooting Solutions) ​ 下载后…

navicat15 恢复试用方法

1.运行,输入regedit,打开注册表 2.注册表中搜索 HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPremium,删除下面的Registration15XCS文件夹 3.注册表中再搜索 HKEY_CURRENT_USER\Software\Classes\CLSID 然后拉到文件夹目录的最后&#x…

实时视频混合和映射:Resolume Arena 6(专业的VJ)中文

Resolume Arena 6是一款专业的实时视频混合和映射软件,广泛应用于音乐表演、舞台演出、俱乐部活动等场合,打造令人惊叹的视觉效果和图像投影。它支持实时混合多个视频源,拥有视频映射功能以及提供多样实时效果和过渡效果,让用户能…

MySQL的MSI安装

MySQL的MSI安装 文章目录: MySQL的MSI安装材料准备:一、MySQL安装的准备工作二、MySQL的配置三、MySQL安装验证1、CMD命名验证2、MySQL 8.0 Command Line Client验证 材料准备: 1、x64或x86电脑 2、MySQL安装包 一、MySQL安装的准备工作 M…

Unity 粒子特效-第一集-五角星发射特效

一、基础理解 1.粒子特效的意思是,发射很多小的东西,组成一个效果 2.主要可以分成两种 a.一直循环(如上图) b.发射状 二、案例视频 今天我们做一个发射的五角星 三、案例分析 我们仔细来分析一下这个五角星的功能 1.每次显示1秒…

Android体育场馆预约系统+全套手把手视频教程

【项目功能介绍】 功能列表: 本系统包含后台管理和前端app双端系统, 本系统包含三个角色: 管理员,员工,app用户。 后台管理员的功能包含: 登录, 退出, 场馆管理,添加场馆,修改场馆,禁用启用场馆; 场馆场地管理,添加场馆场地,修改场馆场地,启用禁用场馆场地; 订单管理,确定订单…

Python自动化运维监控——批量监听页面发邮件(自由配置ini文件+smtplib)

一、程序样式 1.listen.ini配置文件 2.监控页面 3.日志 二、核心点 smtplib库:这里使用了smtp.qq.com与smtp.163.com两个发送邮件的地址,使用邮箱用户名与授权码来实现登录,端口都使用465,最后抛出异常,finally里…

分享一下微信小程序抽奖链接怎么做

标题:微信小程序抽奖链接制作全攻略,轻松玩转营销抽奖活动 一、引言 在当今的数字化时代,抽奖活动已经成为一种高效的市场营销策略,而微信小程序作为一个功能强大的移动端平台,为企业和个人提供了制作抽奖链接的便捷…

如何能够根据现有业务,结合代码清晰的知道技术实现是如何去实现的,方法论和切入点是什么?

要深入了解现有业务系统的技术实现,特别是当你想要准确理解某个具体功能或组件是如何实现的时候,你可以采取以下的方法论和切入点: 1. 文档和设计说明 首先查阅相关的技术文档和设计说明,这些文档通常会详细描述系统的架构、各个…

STM32———USART串口控制LED灯亮灭

1.硬件设计流程 2.程序设计流程 1.串口初始化时钟使能:RCC_APBxPeriphClockCmd(); GPIO初始化时钟使能:RCC_AHBxPeriphClockCmd();2.GPIO端口模式配置:GPIO_Init();3.串口参数初始化:USART_Init();4.串口使能:USART_C…

如何在React项目中引用less

安装less npm install less less-loader --save-dev暴露 webpack 文件 利用 npx create-react-app 搭建的 React 项目,默认隐藏 webpack 配置文件,引入 less 需要修改 webpack 配置文件,因此我们需要执行命令暴露 webpack 配置文件。 请先将…

SRM系统采购功能

SRM系统采购是企业为了优化供应链管理而采购的一种信息系统。它旨在帮助企业与供应商之间建立良好的合作关系,提供全面的供应商管理功能,以实现采购的效率和效益最大化。 首先,SRM系统采购在企业供应链管理中具有重要的意义。供应链是企业与供…

2023最新版本 FreeRTOS教程 -1-标准库移植FreeRTOS

源码下载 官网下载驱动 点击直达 源码剪裁 剪裁之后的图片,找我免费获取 添加进MDK 配置滴答定时器 全部工程获取 查看下方头像

pycharm使用ssh连接远程jupyter

1. 安装jupyter pip install jupyter2. 生成jupyter_notebook_config.py文件 jupyter notebook --generate-config3. 设置命令参数 jupyter notebook --no-browser --allow-root --port 8900配置Jupyter服务器 将上面的代码复制到命令行实参中:

[NISACTF 2022]is secret RC4加密执行SSTI

这里结合了加密 记录一下 首先获取 啥都没得 扫一下目录 都看看 后面发现 http://node5.anna.nssctf.cn:28378/secret 这个页面 Tell me 是不是就是传参 我们get传递一个看看 http://node5.anna.nssctf.cn:28378/secret?secret{{49}} 发生报错 出现了 框架 这…

企业内部外网向内网传输文件如何实现高效安全?

随着信息技术的发展,企业内部外网隔离已成为一种常见的网络安全措施,旨在防止外部攻击者入侵内部网络,保护企业的核心数据和业务系统。然而,企业内外网隔离也带来了一些问题,其中之一就是如何实现内外网之间的文件传输…