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:
    
    //构造器中初始化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++设计模式——状态模式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux网络编程二(TCP三次握手、四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

TCP三次握手 TCP三次握手(TCP three-way handshake)是TCP协议建立可靠连接的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的详细过程: 假设客户端为A,服务器为B 1、第一次握手(SYN1,seq500&…

深度学习_1 介绍;安装环境

深度学习 学习自李沐老师的课程。笔记主要以总结老师所讲解的内容以及我个人的想法为主,侵删! 课程链接:课程安排 - 动手学深度学习课程 (d2l.ai) 介绍 AI地图: 我们以前写的非 AI 类程序基本都是人自己去想会遇到什么样的问题…

集成测试、单元测试、系统测试之间的关系和区别

前言 为了使软件正常工作,所有单元都应集成在一起并正常运行。集成测试就像是要求不同工种的工人修建一个房子,希望他们都团结协作。如何判断他们在一起是否可以按照计划完成建设呢?唯一了解的方法是通过将它们全部拉在一起并测试它们如何相互…

Redis入门01-简单了解

目录 Redis的发展历史 特性简介 数据类型 内存存储与持久化 功能丰富 简单稳定 应用场景 为啥用Redis Redis的发展历史 Redis(Remote Dictionary Server)是一个高性能的键值存储系统,通常用作缓存、消息队列和分布式数据存储的解决方…

数据统计--图形报表--ApacheEcharts技术 --苍穹外卖day10

Apache Echarts 营业额统计 重点:已完成订单金额要排除其他状态的金额 根据时间选择区间 设计vo用于后端向前端传输数据,dto用于后端接收前端发送的数据 GetMapping("/turnoverStatistics")ApiOperation("营业额统计")public Result<TurnoverReportVO…

更新电脑显卡驱动的操作方法有哪些?

更新显卡驱动可以有效的提升我们电脑的性能&#xff0c;可以通过设备管理器、显卡驱动软件等方式进行检查驱动是否需要更新&#xff0c;并修复一些电脑上已知的显卡问题。 然而&#xff0c;对于一些不是很懂电脑技术的人员来说&#xff0c;更新电脑显卡驱动是一件比较复杂和混乱…

Linux服务器部署带Cplex的Java项目

Linux版Cplex安装 Cplex安装包 Cplex 22.1.0 Linux安装包 安装步骤 找到安装包的路径 [roothecs-327697 ~]# cd /www/cplex [roothecs-327697 cplex]# ls cplex_studio2210.linux_x86_64.bin使用chmod 777赋予安装包读、写、执行权限&#xff0c;使用./执行安装 [roothec…

C/S架构和B/S架构

1. C/S架构和B/S架构简介 C/S 架构&#xff08;Client/Server Architecture&#xff09;和 B/S 架构&#xff08;Browser/Server Architecture&#xff09;是两种不同的软件架构模式&#xff0c;它们描述了客户端和服务器之间的关系以及数据交互的方式。 C/S 架构&#xff08…

华为数通方向HCIP-DataCom H12-831题库(多选题:101-120)

第101题 LSR对收到的标签进行保留,且保留方式有多种,那么以下关于LDP标签保留一自由方式的说法 A、保留邻居发送来的所有标签 B、需要更多的内存和标签空间 C、只保留来自下一跳邻居的标签,丢弃所有非下一跳铃邻居发来的标签 D、节省内存和标签空间 E、当IP路由收敛、下一跳…

CVPR 2023 | 主干网络FasterNet 核心解读 代码分析

本文分享来自CVPR 2023的论文&#xff0c;提出了一种快速的主干网络&#xff0c;名为FasterNet。 论文提出了一种新的卷积算子&#xff0c;partial convolution&#xff0c;部分卷积(PConv)&#xff0c;通过减少冗余计算和内存访问来更有效地提取空间特征。 创新在于部分卷积…

Java List Set Map

一、List 1.1 ArrayList 1.2 LinkedList 二、Set 2.1 HashSet 2.2 TreeSet 2.3 LinkedHashSet 三、Map 3.1 HashMap 3.2 TreeMap 3.3 LinkedHashMap 四、对比 类型底层结构重复null值场景备注查询删除新增ListArrayList动态数组可允许快速随机访问元素0(1)0(n)尾部增加0&a…

Yakit工具篇:WebFuzzer模块之重放和爆破

简介 Yakit的Web Fuzzer模块支持用户自定义HTTP原文发送请求。为了让用户使用简单&#xff0c;符合直觉&#xff0c;只需要关心数据相关信息&#xff0c;Yakit后端(yaklang)做了很多工作。 首先我们先来学习重放请求的操作&#xff0c;在日常工作中可以使用 Web Fuzzer进行请…

Star History 九月开源精选 |开源 GitHub Copilot 替代

虽然大火了近一年&#xff0c;但是截至目前 AI 唯一破圈的场景是帮助写代码&#xff08;谷歌云旗下的 DORA 年度报告也给 AI 泼了盆冷水&#xff09;。不过对于软件开发来说&#xff0c;生成式人工智能绝对已经是新的标配。 本期 Star History 收集了一些开源 GitHub Copilot …

虚拟化的基础知识

目录 虚拟化基础 虚拟化的概念 虚拟化的特征&#xff08;本质&#xff09; 虚拟机的两大派别 VMM讲解 虚拟化中的一些重要概念 VMM的功能以及分类 虚拟化的架构 寄居虚拟化 裸金属虚拟化 操作系统虚拟化 混合虚拟化 虚拟化的三个方向 虚拟化基础 虚拟化的概念 什…

元素内容必须由格式正确的字符数据或标记组成

一、错误介绍 前两天我在使用Mybatis开发&#xff0c;进行数据查询时&#xff0c; 系统出现了红色提示&#xff0c;如下图所示&#xff1a; 由提示可知&#xff0c;系统将我的小于号【<】解析为Tag标签了 &#xff0c;而大于号【>】则没有这个问题。为了验证大于号【>…

文件上传自动化测试方案

一、概述 【测试地址】&#xff1a;https://pan.baidu.com 【测试工具】selenium、requests 【脚本语言】Python 【运行环境】Windows 百度网盘作为文件存储及分享的平台&#xff0c;核心功能大部分是对文件的操作&#xff0c;如果要对它进行自动化测试&#xff0c;优先覆…

一带一路10周年:爱创科技加速中国药企国际化征程

“源自中国&#xff0c;属于世界”。 共建“一带一路”倡议提出10周年来&#xff0c;中国与沿线国家经济深度融合&#xff0c;在共商共建共享的基本原则下&#xff0c;“一带一路”形成了国际合作的平台和机制&#xff0c;跨国经济合作已基本形成。 随着“一带一路”合作日益加…

linux系统的环境变量-搞清环境变量到底是什么

环境变量 引例环境变量常见的环境变量echoexportenvunsetset 通过代码获取环境变量使用第三个参数获取使用全局变量enviorn获取环境变量通过系统调用获取环境变量 环境变量具有全局属性main函数前两个参数的作用 引例 在linux系统中&#xff0c;我们使用ls命令&#xff0c;直接…

重磅消息!优维发布全新产品“应急管理”

近日&#xff0c;蚂蚁集团旗下的在线文档编辑与协同工具语雀平台发生了一次严重的宕机事件&#xff0c;导致用户无法正常使用其各项功能。从故障发生到完全恢复正常&#xff0c;语雀整个宕机时间将近 8 小时&#xff0c;如此长时间的宕机已经达到了 P0 级事故&#xff0c;并在网…

YugaByteDB -- 全新的 “PostgreSQL“ 存储层

文章目录 0 背景1 架构1.1 Master1.2 TServer1.3 Tablet 2 读写链路2.1 DDL2.2 DML2.3 事务 3 KEY 的设计4 Rocksdb 在 YB 中的一些实践总结 0 背景 YugaByteDB 的诞生也是抓住了 spanner 推行的NewSQL 浪潮的尾巴&#xff0c;以 PG 生态为基础 用C实现的 支持 SQL 以及 CQL 语…