重拾设计模式--状态模式

embedded/2024/12/23 14:57:41/

文章目录

  • 状态模式(State Pattern)概述
  • 状态模式UML图
  • 作用:
  • 状态模式的结构
    • 环境(Context)类:
    • 抽象状态(State)类:
    • 具体状态(Concrete State)类:
  • C++ 代码示例1
  • C++示例代码2
  • 应用场景
    • 游戏开发领域
    • 图形用户界面(GUI)系统
    • 工作流系统和业务流程管理
    • 网络协议和通信系统

状态模式(State Pattern)概述

定义:
状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为。就好像对象看起来修改了它的类一样,通过将不同状态对应的行为封装到不同的状态类中,让一个对象在不同的状态下可以表现出不同的行为,而这些行为的切换是由对象的状态变化来驱动的。

状态模式UML图

在这里插入图片描述

作用:

提高代码的可维护性和可扩展性:当一个对象有多种状态,且每种状态下行为差异较大时,如果使用大量的条件判断语句(如if-else或者switch语句)来处理不同状态下的行为,代码会变得复杂且难以维护。而状态模式把不同状态的行为分散到各个对应的状态类中,后续添加新状态或者修改某个状态下的行为就比较方便,只需对相应的状态类进行操作,不会影响到其他部分的代码。
符合开闭原则:对扩展开放,对修改关闭。例如在一个游戏角色有多种状态(站立、行走、攻击等)的场景中,若要新增一种 “跳跃” 状态以及对应的行为,只需要新增一个 “跳跃” 状态类实现相关行为逻辑,而不用去大规模修改原有的代码结构,原有的状态类和使用状态的对象代码都可以保持相对稳定。
增强代码的可读性:不同状态下的行为逻辑清晰地封装在各自的状态类中,阅读代码时可以更直观地了解每个状态对应的具体操作,相比于把所有状态相关行为混杂在一个类里的写法,结构更加清晰明了。

状态模式的结构

环境(Context)类:

它定义了客户端感兴趣的接口,并且维护一个具体状态类的实例,这个实例代表了当前对象所处的状态。环境类的行为会受到其内部状态对象的影响,它会将请求委托给当前状态对象来处理。

抽象状态(State)类:

它定义了一个接口,这个接口包含了那些在不同具体状态下对象可能执行的方法,所有的具体状态类都要实现这个抽象状态类所定义的接口,来提供各自状态下的具体行为逻辑。

具体状态(Concrete State)类:

实现了抽象状态类中定义的接口,针对具体的某一种状态,提供了该状态下相应行为的具体实现。每个具体状态类实现的行为会根据具体业务需求而不同,当环境类的状态切换到某个具体状态时,环境类发出的请求就会由对应的这个具体状态类来处理。

C++ 代码示例1

以下以一个简单的电梯控制系统为例来展示状态模式的代码实现。电梯有几种不同的状态,比如静止、上升、下降等,每种状态下对于外部的请求(如按楼层按钮等)会有不同的响应行为。

#include <iostream>// 抽象状态类
class ElevatorState
{
public:virtual void open() = 0;virtual void close() = 0;virtual void run() = 0;virtual void stop() = 0;
};// 具体状态类:静止状态
class StoppedState : public ElevatorState 
{
public:void open() override{std::cout << "电梯门打开,当前处于静止状态。" << std::endl;}void close() override {std::cout << "电梯门关闭,当前处于静止状态,准备运行。" << std::endl;}void run() override{std::cout << "电梯从静止状态开始运行。" << std::endl;}void stop() override {std::cout << "电梯已经处于静止状态,无需再次停止。" << std::endl;}
};// 具体状态类:上升状态
class RisingState : public ElevatorState 
{
public:void open() override{std::cout << "电梯正在上升,不能开门。" << std::endl;}void close() override {std::cout << "电梯正在上升,门已关闭。" << std::endl;}void run() override{std::cout << "电梯继续上升。" << std::endl;}void stop() override{std::cout << "电梯上升过程中停止。" << std::endl;// 假设停止后切换到静止状态,可以在这里做相应状态切换逻辑,比如通知环境类切换状态}
};// 具体状态类:下降状态
class FallingState : public ElevatorState 
{
public:void open() override {std::cout << "电梯正在下降,不能开门。" << std::endl;}void close() override{std::cout << "电梯正在下降,门已关闭。" << std::endl;}void run() override {std::cout << "电梯继续下降。" << std::endl;}void stop() override{std::cout << "电梯下降过程中停止。" << std::endl;// 同样,停止后可考虑切换状态逻辑}
};// 环境类:电梯
class Elevator
{
private:ElevatorState* currentState;
public:Elevator(){currentState = new StoppedState();}void setCurrentState(ElevatorState* state) {currentState = state;}void open(){currentState->open();}void close(){currentState->close();}void run() {currentState->run();}void stop() {currentState->stop();}
};int main()
{Elevator elevator;elevator.open();elevator.close();elevator.run();elevator.stop();RisingState risingState;elevator.setCurrentState(&risingState);elevator.open();elevator.close();elevator.run();elevator.stop();return 0;
}

C++示例代码2

#include<iostream>
#include<list>
#include<string>
using namespace std;
class Andy;//男主
class State//状态
{
public:string m_satedes;//描述当前的状态
public:virtual void Fbegin(){};//前期状态virtual void Fmid(){};//中期状态virtual void Fend(){};//末期状态virtual void CurrentState(Andy*p_andy){};//调用当前状态,统一的接口,然后各个状态子类去调用自己的函数
};
class Andy
{
private:State *m_state;  //目前状态int m_years;      //时间
public:Andy(State *state): m_state(state), m_years(0) {}~Andy() { delete m_state; }int GetYears() { return m_years; }void SetYears(int years) { m_years = years; }void SetState(State *state) { delete m_state; m_state = state; }void GetState() { m_state->CurrentState(this); }
};//服刑末期
class State_End:public State
{
public:State_End(){m_satedes = "挖洞逃出来了";}void FEnd(Andy *p_andy){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}void CurrentState(Andy *p_andy){FEnd(p_andy);}
};//服刑中期
class State_Mid:public State
{
public:State_Mid(){m_satedes = "服刑中期,每天帮监狱长做假账,顺便开始夜里挖洞";}void FMid(Andy *p_andy){if(p_andy->GetYears()<28){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}else{p_andy->SetState(new State_End());p_andy->GetState();}}void CurrentState(Andy *p_andy){FMid(p_andy);}
};//服刑前期
class State_begin:public State
{
public:State_begin(){m_satedes = "男主Andy刚进监狱,处处受人欺负";}void Fbegin(Andy *p_andy){if(p_andy->GetYears()<5){cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;}else{p_andy->SetState(new State_Mid());p_andy->GetState();}}void CurrentState(Andy *p_andy){Fbegin(p_andy);}
};int main()
{Andy *p = new Andy(new (State_begin));for(int i = 1; i < 40; ++i){p->SetYears(i);p->GetState();}delete p;return 0;
}

应用场景

游戏开发领域

角色状态管理:游戏中的角色通常有多种状态,如站立、行走、奔跑、跳跃、攻击、受伤、死亡等。使用状态模式可以为每个状态创建一个单独的类,在这些类中实现角色在该状态下的行为,如移动速度、动画播放、可执行的操作等。例如,当角色处于攻击状态时,其移动速度可能会降低,并且会播放攻击动画,同时能够触发攻击相关的逻辑,如伤害计算。
游戏场景状态切换:游戏场景也可以有不同的状态,像游戏的主菜单场景、游戏进行中的场景、暂停场景、游戏结束场景等。每个场景状态都有自己的一套显示内容、交互逻辑和更新机制。通过状态模式,可以方便地在不同场景状态之间切换,并且清晰地管理每个状态下的资源加载、渲染和事件处理。

图形用户界面(GUI)系统

窗口状态管理:一个窗口可能有多种状态,如最小化、最大化、正常、隐藏等。在不同状态下,窗口的显示方式、对用户操作的响应等都不同。利用状态模式,可以将每个状态对应的行为封装到相应的状态类中。例如,当窗口处于最小化状态时,它可能只在任务栏显示一个图标,并且点击图标时的操作(如恢复窗口大小)与窗口处于正常状态时的操作(如拖动边框改变大小)是不同的。
按钮状态控制:按钮有正常、鼠标悬停、按下等状态。在不同状态下,按钮的外观(如颜色、样式)和行为(如触发的事件)不同。状态模式可以让开发者轻松地定义每个状态下按钮的渲染逻辑和事件响应逻辑,使得按钮的状态管理更加清晰和易于维护。

工作流系统和业务流程管理

订单处理流程:在电商系统中,订单有多种状态,如已创建、已付款、已发货、已签收、已退款等。每个状态下的订单处理逻辑不同,例如在 “已付款” 状态下,系统会通知仓库准备发货;在 “已发货” 状态下,系统会提供物流信息查询等服务。通过状态模式,可以把订单在不同状态下的处理逻辑封装在对应的状态类中,方便业务流程的扩展和维护。
审批流程:企业中的审批流程也存在多种状态,如提交审批、部门主管审批中、高层领导审批中、审批通过、审批拒绝等。对于每个状态,都有相应的操作和流转规则,比如在 “部门主管审批中” 状态下,部门主管可以查看申请内容并进行审批操作,审批通过后状态会切换到 “高层领导审批中”。使用状态模式可以清晰地实现这种复杂审批流程的状态管理和操作逻辑。

网络协议和通信系统

TCP 连接状态管理:在 TCP 协议中,连接有多种状态,如 LISTEN(监听)、SYN - SENT(同步已发送)、SYN - RECEIVED(同步收到)、ESTABLISHED(已建立)、FIN - WAIT - 1(终止等待 1)等。在不同状态下,网络设备(如服务器和客户端)的行为和数据处理方式不同。状态模式可以用于实现 TCP 连接状态的管理,将每个状态对应的数据包处理、连接维护等操作封装在相应的状态类中,使得网络通信的状态处理更加清晰和可靠。
设备通信状态:在物联网系统中,设备之间的通信可能会出现多种状态,如连接正常、连接中断、数据传输中、等待响应等。对于每种状态,可以使用状态模式来定义设备在该状态下的通信行为,如重新连接策略、数据缓存和发送方式等,以确保设备之间的通信稳定和高效。


http://www.ppmy.cn/embedded/147731.html

相关文章

手机便签哪个好用?手机桌面便签app下载推荐

在快节奏的现代生活中&#xff0c;我们常常需要记录一些重要的信息和灵感&#xff0c;以便于日后查阅和回顾。手机便签软件因其便携性和易用性&#xff0c;成为了我们日常生活中不可或缺的工具。无论是购物清单、待办事项、灵感记录还是重要笔记&#xff0c;手机便签都能帮助我…

一步一步写线程之十六线程的安全退出之二例程

一、说明 在一篇分析了多线程的安全退出的相关机制和方式&#xff0c;那么本篇就针对前一篇的相关的分析进行举例分析。因为有些方法实现的方法类似&#xff0c;可能就不一一重复列举了&#xff0c;相关的例程主要以在Linux上的运行为主。 二、实例 线程间的同步&#xff0c…

汇编DOSBox 如何使文件可以运行

1.在vscode编写&#xff08;其他也可以&#xff09;如何在vscode中编写汇编语言并在终端进行调试(保姆级别&#xff09;_如何在vscode编译asm-CSDN博客 2.点击ML615中的DOS 2.1在命令行中输入命令 ml 文件名.asm ml 文件名.obj 2.2 将生成的exe文件移动到Assembly里面 这个文件…

3D开发工具HOOPS助力造船业加速设计与数字化转型

随着造船业对设计、高效精准建模和快速原型开发的需求不断增加&#xff0c;先进的3D技术逐渐成为推动行业创新的核心工具。Tech Soft 3D的HOOPS SDK作为行业领先的开发平台&#xff0c;已被NAPA、Herbert-ABS和三菱造船有限公司等全球造船业领导者所采用&#xff0c;帮助企业加…

双系统完美卸载Ubuntu

使用DiskGenius删除ubuntu的分区 删除引导项 diskpart list disk 查看C盘所在磁盘 select disk 1 选择指定的磁盘&#xff08;磁盘1 list partition 列出该磁盘上的所有分区 select partition 1 指定EFI系统分区 assign letterJ 给分区分配新盘符&#xff0c;电脑重启后J盘会自…

CMake 构建实例

CMake 构建步骤如下&#xff1a; 创建 CMakeLists.txt 文件&#xff1a;定义项目、目标和依赖。创建构建目录&#xff1a;保持源代码目录整洁。配置项目&#xff1a;使用 CMake 生成构建系统文件。编译项目&#xff1a;使用构建系统文件编译项目。运行可执行文件&#xff1a;执…

电脑问题4[非华为电脑安装华为电脑管家华为荣耀手机多屏协助]

非华为电脑安装华为电脑管家华为荣耀手机多屏协助 我是荣耀手机之前一直用的是window的"连接手机"功能,电脑控制手机还蛮好用,但是又不能够没有好的电脑控制手机的功能,后来想了想看了看,竟然安装了华为电脑关键,竟然可以顺利连接上荣耀手机,发现还蛮好用! 本文引用…

JavaScript网络请求( XMLHttpRequest 对象,进度事件, 跨源资源共享)

一、 XMLHttpRequest 对象 IE5 是第一个引入 XHR 对象的浏览器。这个对象是通过 ActiveX 对象实现并包含在 MSXML 库中 的。为此&#xff0c; XHR 对象的 3 个版本在浏览器中分别被暴露为 MSXML2.XMLHttp 、 MSXML2.XMLHttp.3.0 和 MXSML2.XMLHttp.6.0 。 所有现代…