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

设计模式专栏: 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.hqwc.cn/news/564704.html

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

相关文章

竞赛 python opencv 深度学习 指纹识别算法实现

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python opencv 深度学习 指纹识别算法实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;4分创新点&#xff1a;4分 该项目较为新颖…

Docker(二):Docker常用命令

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

linux系统编程 socket part2

报式套接字 1.动态报式套接字2.报式套接字的广播3.报式套接字的多播4.UDP协议分析4.1.丢包原因4.2.停等式流量控制 接linux系统编程 socket part1 1.动态报式套接字 在之前的例子上&#xff0c;发送的结构体中的名字由定长改变长。可以用变长结构体。 变长结构体是由gcc扩展的…

蓝桥杯基础练习详细解析一(代码实现、解题思路、Python)

试题 基础练习 数列排序 资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 输入格式 第…

对于组件通信的深刻理解

父组件传递数据给子组件 props传递数据 父组件在子组件的标签上写自定义的属性,属性值是自己的变量,当渲染到子组件时,执行props会找自定义属性,内存了变量的内存,可访问到,写props,会生成vue实例的时候,将props的变量赋给,值找变量内存存入变量.插值语句等可访问.父组件会变…

android Fragment 生命周期 方法调用顺序

文章目录 Introlog 及结论代码 Intro 界面设计&#xff1a;点击左侧按钮&#xff0c;会将右侧 青色的RightFragment 替换成 黄色的AnotherRightFragment&#xff0c;而这两个 Fragment 的生命周期方法都会打印日志。 所以只要看执行结果中的日志&#xff0c;就可以知道 Fragme…

DBA工作经验总结

目录 一、MySQL8.0创建一张规范的表 1.表、字段全采用小写 2.int类型不再加上最大显示宽度 3.每张表必须显式定义自增int类型的主键 4.建表时增加comment来描述字段和表的含义&#xff08;防止以后忘记&#xff09; 5.建议包含create_time和update_time字段 6.核心业务增…

Godot 学习笔记(5):彻底的项目工程化,解决GodotProjectDir is null+工程化范例

文章目录 前言GodotProjectDir is null解决方法解决警告问题根本解决代码问题测试引用其实其它库的输出路径无所谓。 工程化范例环境命名规范Nuget项目结构架构代码ISceneModelIOC服务 测试GD_Extension 通用扩展TestUtils GD_ProgramTestServiceMainSceneModel Godot对应的脚本…

信号处理与分析——matlab记录

一、绘制信号分析频谱 1.代码 % 生成测试信号 Fs 3000; % 采样频率 t 0:1/Fs:1-1/Fs; % 时间向量 x1 1*sin(2*pi*50*t) 1*sin(2*pi*60*t); % 信号1 x2 1*sin(2*pi*150*t)1*sin(2*pi*270*t); % 信号2% 绘制信号图 subplot(2,2,1); plot(t,x1); title(信号x1 1*sin(…

MySQL数据库基本操作(增删改查)与用户授权

前言 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理关系数据库系统的语言。SQL的设计目标是提供一种简单、直观的语言&#xff0c;使得用户可以通过编写SQL语句来处理他们想要的数据和操作。 目录 一、结构介绍 1. 查看信…

银行监管报送系统介绍(五):金融统计数据大集中自动化报送系统——PBOC Report

人民银行金融统计数据大集中自动化报送系统&#xff08;简称PBOC Report&#xff09;&#xff0c;是基于现代计算机网络技术应用基础上&#xff0c;由人行总行设置金融统计数据服务器&#xff0c;建立的一个全国统一的金融统计数据库。 人行针对各银行存贷款、中间业务、网点人…

学习刷题-13

3.23 hw机试【二叉树】 剑指offer32 剑指 offer32&#xff08;一、二、三&#xff09;_剑指offer 32-CSDN博客 从上到下打印二叉树I 一棵圣诞树记作根节点为 root 的二叉树&#xff0c;节点值为该位置装饰彩灯的颜色编号。请按照从 左 到 右 的顺序返回每一层彩灯编号。 输…