设计模式之状态模式(一)

news2024/11/16 21:01:52

设计模式专栏: http://t.csdnimg.cn/4Mt4u

目录

1.概述  

2.结构

3.实现

4.总结


1.概述  

        状态模式( State Pattern)也称为状态机模式( State Machine pattern), 是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类, 属于行为型模式。

        在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

        所谓状态机,就是当一个对象状态转换的条件表达式过于复杂的时候,把状态的判断逻辑转换到不同状态的一系列类当中去。这样解释可能有点抽象,我们举一个简单的例子,我们以电梯为例,电梯可以分成开门,关门,上升/下落,停止这五个部分。首先我们要明确两点,就是首先这五种状态在同一时间只能出现一个,其次,这五种状态在满足某种条件后是可以相互转换的,比如下落到某楼层后就会进入停止状态,那么这也是状态机使用的两个前提,第一,在某段时间内只准许出现一种状态,第二,这些状态在满足某些条件后是可以相互转换的。这其实就有点类似算法中的有限状态机的形式。

2.结构

        状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。状态模式的UML类图如下所示:

角色定义:

抽象状态角色(State): 接口或抽象类,复杂状态定义,并且封装环境角色以实现状态切换。

具体状态角色(ConcreteState): 每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说, 就是本状态下要做的事情,以及本状态如何过渡到其他状态。

环境角色(Context): 也称上下文,定义客户端需要的接口,并且负责具体状态的切换。

注意:编程中提到的上下文(context),可以理解为环境语境,每一段程序都有很多的外部变量,一旦写的一段程序中有了外部变量,这段程序就是不完整的,不能独立运行,要想让他运行,就必须把所有的外部变量的值一个一个的全部传进去,这些值的集合就叫作上下文。

3.实现

简单的流程示例代码如下:

#include <iostream>
#include <memory>

class Context;

class IState // 抽象状态接口类
{
public:
	virtual ~IState() {}
	virtual void handle(Context *context) = 0; // 传入上下文类接口,处理完后改变当前状态(可以理解为设置为下一状态)
};

// 具体状态接口类A
class ConcreteStateA : public IState 
{
public:
	void handle(Context *context) override{ // 要在类Context声明后面定义,否则会提示使用了未定义“Context”
        cout << "ConcreteStateA::handle()" << endl; // 添加处理这个状态的逻辑功能代码
	    context->changeState(std::make_shared<ConcreteStateB>());
    }
};

// 具体状态接口类B
class ConcreteStateB : public IState 
{
public:
	void handle(Context *context) override{
        cout << "ConcreteStateB::handle()" << endl; // 添加处理这个状态的逻辑功能代码
	    context->changeState(std::make_shared<ConcreteStateC>());
    }
};

// 具体状态接口类C
class ConcreteStateC : public IState 
{
public:
	void handle(Context *context) override{
        cout << "ConcreteStateC::handle()" << endl; // 添加处理这个状态的逻辑功能代码
	    context->changeState(std::make_shared<ConcreteStateA>());
    }
};

//上下文类
class Context
{
	std::shared_ptr<IState> m_pState;
public:
	explicit Context(std::shared_ptr<IState> pState) :m_pState(pState) {}
	~Context() { } 
	void request() { m_pState->handle(this); } // 委托处理函数
	void changeState(std::shared_ptr<IState> pState) { // 改变状态
		this->m_pState= pState;
    } 
};

int main(){
    // 初始化Context对象,Context内部实现具体状态ConcreteState内存管理,不需要手动释放
	std::unique_ptr<Context> pContext(new Context(std::make_shared<ConcreteStateA>()));
	
	context->request(); // 状态请求
	context->request();
	context->request();
	context->request();
    
    return 0;
}

上述实例展示的是:A->B->C->A 状态转移过程,这是由3个状态组成的环状转移图,非常好懂,也非常简单,用他展示状态模式非常形象。一些简单的好理解的状态图,非常适合状态模式去“解耦”。

4.总结

优点

  1. 清晰的结构和逻辑:状态机模式使得对象的行为与其状态紧密相关,使得代码结构更加清晰,逻辑更加明确。每个状态及其转换都被明确地定义和封装,使得程序易于理解和维护。

  2. 扩展性好:当需要添加新的状态或修改现有状态的行为时,只需要添加新的状态类或者修改现有状态类的实现,而不需要修改上下文或其他状态类的代码。这降低了代码的耦合度,提高了系统的可扩展性。

  3. 减少条件分支:状态机模式通过将状态转换逻辑封装在状态类中,减少了在上下文中使用大量的条件分支(如if-else或switch-case)的情况。这有助于减少代码的复杂性,提高可读性。

  4. 适用于复杂逻辑:对于具有复杂状态转换逻辑的系统,状态机模式能够提供一个清晰的框架来组织和管理这些逻辑,使得系统更加健壮和可靠。

缺点

  1. 可能增加类的数量:每个状态都需要一个单独的状态类,这可能会导致类的数量增加,从而增加系统的复杂性。然而,这可以通过合理的包结构和命名约定来管理。

  2. 可能引发状态泄漏:如果在状态转换过程中没有正确地管理状态对象的生命周期,可能会导致内存泄漏。因此,在实现状态机模式时,需要特别注意状态对象的创建和销毁。

  3. 可能增加开发和调试难度:对于不熟悉状态机模式的开发人员来说,理解和实现状态机可能会增加开发和调试的难度。然而,通过提供清晰的文档和示例代码,可以降低这种难度。

  4. 性能考虑:在某些情况下,频繁的状态转换可能会导致性能下降,特别是在处理大量事件或需要快速响应的场景中。然而,通过优化状态转换逻辑和使用合适的数据结构,可以减轻这种性能影响。

综上所述,状态机模式具有清晰的结构和逻辑、良好的扩展性等优点,但也可能带来类数量增加、状态泄漏等缺点。在选择是否使用状态机模式时,需要根据具体的应用场景和需求进行权衡。

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

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

相关文章

<商务世界>《第16课 餐桌礼仪之座次》

1 简要 我国自古以来就很重视座位礼仪&#xff0c;非讲究&#xff0c;分君臣、分宾主、分方位等等而今座位礼仪已经简化为&#xff1a; 以“中”为尊&#xff1a; 中心为尊&#xff0c;突出主位。 以“右”为尊&#xff1a; 从历史上到国际上都是以右为尊。 以“内”为尊&…

算法导论第十四章练习参考答案(26) - 14.1-14.3

Exercise 14.1-1 呼叫顺序为: OS−SELECT(T.root,10) OS−SELECT(T.root.left&#xff0c;10) OS−SELECT(T.root.left.right&#xff0c;2) OS−SELECT(T.root.left.right.left&#xff0c;2) OS−SELECT(T.root.left.right.left.right&#xff0c;1) 然后&#xff0c;我们得到…

UKP3d的协同设计相关问题

用户在用UKP3d多人协同设计&#xff0c;反映以前保存的内容为什么没有呢&#xff1f; 经查&#xff0c;协同设计的某一用户并没有打开协同去用。如A,B两人协同设计&#xff0c;B并不是用“打开—协同项目”&#xff0c;而是用“打开—项目”&#xff0c;当B保存项目的时候&…

RelativeContainer踩坑--子控件消失

使用android的RelativeLayout时&#xff0c;靠左靠上的子控件&#xff0c;我通常不会去声明它和父布局的约束关系。 结果这个方法用到鸿蒙RelativeContainer上出了问题&#xff0c;当我写第一个子控件时&#xff0c;没有声明与父布局的约束关系&#xff0c;显示是OK的 Relati…

NX二次开发-调内部函数创建进度条MT_create_progress_bar

一、概述 最近学习NX二次开发&#xff0c;看到NX打开装配模型或者加载模型时会显示进度条的问题&#xff0c;个人觉得很有意思&#xff0c;然后参考阿飞2018中的文章进行学习。 二、代码解析 //User Defined Header File#include <uf.h>#include <uf_ui.h>#includ…

Zookeeper(八)序列化与协议

目录 一 序列化与反序列化1.1 Jute序列化工具1.1 Recor接口1.2 OutputArchive和InputArchive 二 通信协议2.1 请求部分2.1.1 请求头2.2.2 请求体2.1.3 案例分析 2.2 响应部分2.2.1 响应头2.2.2 响应内容2.2.3 案例分析 官网&#xff1a;Apache ZooKeeper 一 序列化与反序列化 …

vivado 查看消息、增量编译消息、查看实施报告

查看消息 重要&#xff01;查看所有消息。这些信息可能会建议如何改进您的设计性能、功率、面积和布线。严重警告还可能暴露时间限制问题必须解决。 以非项目模式查看消息 在非项目模式下&#xff0c;查看Vivado日志文件&#xff08;Vivado.log&#xff09;中的以下内容&…

QToolButton 设置图标变灰

1、目的 使用一张图片来实现QToolButton控件两种状态&#xff08;ON和OFF状态&#xff09;的图标。前提不能使用两张图片&#xff0c;也不能使用setEnable来图标变灰&#xff0c;因为当设置了false之后&#xff0c;控件将不能再切换了。 2、方法 知道可以通过QToolButton有s…

Python编程—Ajax数据爬取

Python编程—Ajax数据爬取 ​ 在浏览器中可以看到正常显示的页面数据&#xff0c;而使用requests得到的结果中并没有这些数据。这是因为requests获取的都是原始HTML文档&#xff0c;而浏览器中的页面是JavaScript处理数据后生成的结果&#xff0c;这些数据有多种来源&#xff…

Docker(二):Docker常用命令

docker 查看docker支持的所有命令和参数。 ➜ ~ docker Management Commands:config Manage Docker configscontainer Manage containersimage Manage imagesnetwork Manage networksnode Manage Swarm nodesplugin Manage pluginssecret …

golang 对接第三方接口 RSA 做签(加密) 验签(解密)

一、过程 1.调用第三方接口前&#xff0c;一般需要按规则将参数按key1value1&key2value2 阿斯克码排序,sign参数不参与加密 2.将排序并连接好的参数字符串通过我方的私钥证书&#xff08;.pem&#xff09;进行加密得到加密串&#xff0c;当然加密得到的是 []byte 字节流&…

vue iframe实现父页面实时调用子页面方法和内容,已解决

父页面标签添加鼠标按下事件 父页方法中建立iframe通信 实时调用子页面方法 实时更改子页面文本内容

Chrome 114 带着侧边栏扩展来了

效果展示 manifest.json {"manifest_version": 3,"name": "ChatGPT学习","version": "0.0.2","description": "ChatGPT,GPT-4,Claude3,Midjourney,Stable Diffusion,AI,人工智能,AI","icons"…

C语言经典算法-9

文章目录 其他经典例题跳转链接46.稀疏矩阵47.多维矩阵转一维矩阵48.上三角、下三角、对称矩阵49.奇数魔方阵50.4N 魔方阵51.2(2N1) 魔方阵 其他经典例题跳转链接 C语言经典算法-1 1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. 三色棋 5. 老鼠走迷官&#xff08;一&#xff09;6.…

【go从入门到精通】for循环控制

作者简介&#xff1a; 高科&#xff0c;先后在 IBM PlatformComputing从事网格计算&#xff0c;淘米网&#xff0c;网易从事游戏服务器开发&#xff0c;拥有丰富的C&#xff0c;go等语言开发经验&#xff0c;mysql&#xff0c;mongo&#xff0c;redis等数据库&#xff0c;设计模…

ssm003在线医疗服务系统+jsp

在线医疗服务系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管…

bevformer转模型经验(需要时序tranformer所有模型都可以参考)

先上bevformer的网络结构图 不难发现&#xff0c;他有两个输入。当前的环视图和历史bev。历史bev是通过历史环视图生成的。也就是说在生成bev特征提取模型这部分被使用了两次。在装模型时候&#xff0c;需要作以下工作&#xff1a; 1 bev特征提取模型单独提出来&#xff0c;转…

用Kimichat学习王庆法老师关于Sora的文章

目录 一 引言:二 提示词方面:三 与Kimi的聊天记录我:假如你是一名大模型方面的专家,提取一下这篇文章的核心观点,用三列表格的形式,https://mp.weixin.qq.com/s/Y-vmxmPu4_-tHaeP35hDJg我:上述文章的一、Spacetime Latent Patches 潜变量时空碎片, 建构视觉语言系统部分…

python处理Excel的方法之xlrd

python处理Excel常用到的模块是xlrd。使用xlrd可以非常方便的处理Excel文档&#xff0c;下面介绍一下基本用法 打开文件 import xlrd data xlrd.open_workbook("c:\\skills.xls") 获取一个工作表 table data.sheet_by_name(uskills) #也可以 table data.sheet_by_…

PTA-练习7

目录 实验8-2-1 找最小的字符串 实验8-2-2 找最长的字符串 实验8-2-8 字符串排序 实验8-2-10 IP地址转换 结构体 实验9-1 计算职工工资 实验9-2 时间换算 实验9-3 计算平均成绩 实验9-5 查找书籍 实验9-8 通讯录排序 实验9-9 有理数比较 实验11-1-1 英文单词排序 …