(十一)Head first design patterns状态模式(c++)

news/2024/9/12 17:19:35/

状态模式

如何去描述状态机?

假设你需要实例化一台电梯,并模仿出电梯的四个状态:开启、关闭、运行、停止。也许你会这么写

class ILift{
public:virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){}
};
class Lift : public ILift{
public:void open(){ std::cout << "电梯门关闭..." << std::endl; }void close(){ std::cout << "电梯门开启..." << std::endl; }void run(){ std::cout << "电梯上下跑起来..." << std::endl; }void stop(){ std::cout << "电梯停止了..." << std::endl; }
};
int main(){ILift* lift = new Lift();lift->open();lift->close();lift->run();lift->stop();
}

这样写未免太草率了。因为电梯在门开启的时候一般是不能运行的,在运行的时候一般也不会门开启,而在停止工作状态一般不会再去执行关门这个动作。所以需要设置一些状态去限制这台电梯的行为。于是在类Lift中存储电梯目前的状态,在执行每个动作的时候用swith分支来判断当前动作是否有效,以及更新当前状态。于是有了下面的代码

class ILift{
public:virtual void setState(int state){};virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){}
};
class Lift : public ILift{
public:Lift(int state):state(state){}void setState(int state){ this->state = state; }void close(){switch(state){case OPENING_STATE:closeWithoutLogic();setState(CLOSING_STATE);break;case CLOSING_STATE:break;case RUNNING_STATE:break;case STOPPING_STATE:break;}}void open(){switch(state){case OPENING_STATE:break;case CLOSING_STATE:openWithoutLogic();setState(OPENING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:openWithoutLogic();setState(OPENING_STATE);}}void run(){switch(state){case OPENING_STATE:break;case CLOSING_STATE:runWithoutLogic();setState(RUNNING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:runWithoutLogic();setState(RUNNING_STATE);}}void stop(){switch(state){case OPENING_STATE:break;case CLOSING_STATE:stopWithoutLogic();setState(STOPPING_STATE);break;case RUNNING_STATE:stopWithoutLogic();setState(STOPPING_STATE);break;case STOPPING_STATE:break;}}void closeWithoutLogic(){ std::cout << "电梯门关闭..." << std::endl; }void openWithoutLogic() { std::cout << "电梯门开启..." << std::endl; }void runWithoutLogic()  { std::cout << "电梯上下跑起来..." << std::endl; }void stopWithoutLogic() { std::cout << "电梯停止了..." << std::endl; }
private:int state;
};
int main(){ILift* lift = new Lift(STATE(OPENING_STATE));lift->close(); // 关闭lift->open();  // 开启lift->run();   // 无动作lift->stop();  // 无动作lift->close(); // 关闭
}

这个类的实现代码特别长,内部包含了太多的switch语句。而且当需要增加状态时,比如说电梯停电状态和电梯维修状态,就需要去更改里面的switch语句。这样写违背了开闭原则以及单一性原则。为了在增加状态的时候尽量少的修改原有代码,可以将swtich中的每个状态抽离出来单独包装成类。

创建context类,在context类中存有LiftState对象用来记录当前的状态。此时context目前拥有四种状态对象,用指针维护。每种状态的逻辑由各自类在内部实现。当电梯发生动作,即context调用函数时,函数内部会调用当前state对应的方法,于是这部分逻辑转交由state内部实现。具体代码如下:

#include<iostream>
#include<string>using namespace std;class ContextBase;
class LiftState{
public:void setContext(ContextBase* context){ this->mContext = context; }virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){}ContextBase* getContext(){ return mContext; }
public:ContextBase* mContext;
};
class ContextBase{
public:ContextBase(){}virtual LiftState* getLiftState(){}virtual LiftState* getOpenningState(){}virtual LiftState* getClosingState(){}virtual LiftState* getRunningState(){}virtual LiftState* getStoppingState(){}virtual void setLiftState(LiftState* liftState){}virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){}
public:LiftState* liftState;
};
class OpenningState : public LiftState{void open(){std::cout << "lift open..." << std::endl;}void close(){mContext->setLiftState(mContext->getClosingState());mContext->getLiftState()->close();}void run(){}void stop(){}
};
class ClosingState : public LiftState{void open(){mContext->setLiftState(mContext->getOpenningState());mContext->getLiftState()->open();}void close(){std::cout << "lift close..." << std::endl;}void run(){mContext->setLiftState(mContext->getRunningState());mContext->getLiftState()->run();}void stop(){mContext->setLiftState(mContext->getStoppingState());mContext->getLiftState()->stop();}
};
class RunningState : public LiftState{void open(){}void close(){}void run(){std::cout << "lift running..." << std::endl;}void stop(){mContext->setLiftState(mContext->getStoppingState());mContext->getLiftState()->stop();}
};
class StoppingState : public LiftState{void open(){mContext->setLiftState(mContext->getOpenningState());mContext->getLiftState()->open();}void close(){}void run(){mContext->setLiftState(mContext->getRunningState());mContext->getLiftState()->run();}void stop(){std::cout << "lift stopping..." << std::endl;}
};class Context : public ContextBase{
public:Context(){}LiftState* getLiftState(){return liftState;}LiftState* getOpenningState(){return openningState;}LiftState* getClosingState(){return closingState;}LiftState* getRunningState(){return runningState;}LiftState* getStoppingState(){return stoppingState;}void setLiftState(LiftState* liftState){this->liftState = liftState;this->liftState->setContext(this);}void open(){ liftState->open(); }void close(){ liftState->close(); }void run(){ liftState->run(); }void stop(){ liftState->stop(); }
public:LiftState* openningState = new OpenningState();LiftState* closingState  = new ClosingState();LiftState* runningState  = new RunningState();LiftState* stoppingState = new StoppingState();
};int main(){Context* context = new Context;context->setLiftState(new ClosingState());context->open();context->close();context->run();context->stop();
}

状态模式的优势:

当由新的状态加入时,只需要扩展子类,而不需要过多地更改原有代码。遵守了开闭原则。

当动作和状态更新等逻辑交由状态类内部实现,实现了单一性设计原则。

参考

Java设计模式——状态模式(STATE PATTERN)_java中state pattern-CSDN博客​​​​​​​


http://www.ppmy.cn/news/1329337.html

相关文章

Spark UI中 Shuffle Exchange 和 BroadcastExchange 中的 dataSize 值为什么不一样

背景 Spark 3.5 最近在看Spark UI 上的一些指标看到一个很有意思的东西, 相邻的Shuffle Exechange 和 BroadcastExechange 中的 datasize 居然不一样&#xff0c; 前者为 765KB, 后者为 64.5MB。差别还不少&#xff0c;中间就增加了一个 AQEShuffleRead 计划 结论 Shuffle E…

【笔记】Helm-3 主题-14 Helm版本支持策略

Helm版本支持策略 该文档描述了在Helm和Kubernetes之间的最大版本偏差。 支持的版本 Helm的版本用x.y.z描述&#xff0c;x是主版本&#xff0c;y是次版本&#xff0c;z是补丁版本&#xff0c;遵循 语义化版本 术语。 Semantic Versioning 2.0.0 | Semantic Versioning Helm项…

实现VLAN之间的路由

原理&#xff1a;路由器子接口 一个接口允许多个VLAN通过&#xff08;避免占用物理路由器接口&#xff09;。 目标 第 1 部分&#xff1a;单臂路由 第 2 部分&#xff1a;配置第三层交换机的路由端口 第 3 部分&#xff1a;带SVI的VLAN间路由 第 4 部分&#xff1a;补充知…

Qt —— 编写Windows截图软件(附源码)

示例效果 源码1 #include "maskwidget.h" #include "qapplication.h"MaskWidget *MaskWidg

mysql生成最近24小时整点时间临时表

文章目录 生成最近24小时整点生成最近30天生成12个月 生成最近24小时整点 SELECT-- 每向下推1行, i比上次减去1b.*, i.*,DATE_FORMAT( DATE_SUB( NOW(), INTERVAL ( -( i : i - 1 ) ) HOUR ), %Y-%m-%d %H:00 ) AS time FROM-- 目的是生成12行数据( SELECTa FROM( SELECT 1 A…

廖清生的云意山水画:梅山文化与国画技法的完美融合

飘逸灵动的云朵&#xff0c;跳跃喷射的火苗&#xff0c;起伏流动的海浪纹理&#xff0c;神兽在山水中禧戏&#xff0c;仙人在云端漫步&#xff0c;杂乱无章、精彩纷呈的色彩&#xff0c;廖清生的云意山水画展现了梅山文化与国画技法的完美融合&#xff0c;他以独特的绘画技法和…

【NVIDIA】Jetson Orin Nano系列:安装中文输入法(最简方法)

1、配置中文语言支持 点击左下角“显示应用程序”&#xff0c;选择“语言支持”&#xff0c;选择后会提示安装中文语言&#xff0c;输入用户密码&#xff0c;即可安装。 安装后的界面&#xff1a; 2、安装中文输入发 sudo apt install ibus-sunpinyin本人在Jetson Orin Na…

CGAL 网格自相交检测

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 “Mesh自相交” 是指一个三维网格(mesh)在其自身内部发生相交的情况。这种情况可能导致在计算机图形学、计算机辅助设计(CAD)或其他模拟和建模领域中出现问题。自相交可能导致不正确的渲染、模拟结果的不准确性…

算法通关村番外篇-面试150题一

大家好我是苏麟 , 今天开始LeetCode面试经典150题 . 大纲 26. 删除有序数组中的重复项80. 删除有序数组中的重复项 II 26. 删除有序数组中的重复项 描述 : 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 …

linux远程服务器上下载模型等

记录一下在使用服务器下载时出现的一些问题和解决方法 conda环境位置&#xff1a; /home/liuyuanyuan/.conda/envs/ hugging face下载的模型保存在【通修改此路径&#xff0c;继续下载】 /home/liuyuanyuan/.cache/huggingface/hub通过镜像下载 阿里源 pip install xxx -i h…

WordPress函数has_tag的介绍及用法示例,判断是否含有指定标签?

我们很多WordPress站点的文章页都会添加相应的标签&#xff0c;在某些场合下我们需要判断当前文章页是否含有指定的标签&#xff0c;那么应该用什么判断函数呢&#xff1f;这个时候就需要用到WordPress函数has_tag()了&#xff0c;下面boke112百科就跟大家介绍一下这个函数及具…

01 Redis的特性

1.1 NoSQL NoSQL&#xff08;“non-relational”&#xff0c; “Not Only SQL”&#xff09;&#xff0c;泛指非关系型的数据库。 键值存储数据库 &#xff1a; 就像 Map 一样的 key-value 对。如Redis文档数据库 &#xff1a; NoSQL 与关系型数据的结合&#xff0c;最像关系…

【VRTK】【PICO】如何快速创建一个用VRTK开发的PICO项目

【背景】 每次新建一个VRTK的PICO项目总是做一些重复工作,于是就想着搞成一个基本的包,把基本的设置都放进去,今后新做项目直接导这个包就行了。 完整资源包请见本篇博客的绑定资源。 【内容简介】 这个包是我为了快速开发基于VRTK的PICO应用设置的基础项目包。每次开发…

杭电网课笔记

技巧 1.判断得数为整数还是小数&#xff0c;可以%1&#xff0c;得数为0是整数 或者用instanceof Integer number 9; // 自动装箱 System.out.println(number instanceof Integer); // 输出&#xff1a;true 2.a * b 最大公约数 * 最小公倍数 LCM 最小公倍数 GCD 最大公…

CmakeList教程

一、CmakeList介绍&#xff1a; cmake 是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。它会通过写的语句自动生成一个MakeFile,从而实现高效编译 二、CmakeList的常用指令 1.指定…

一些aarch64 pwn

从x86_64过来&#xff0c;把这个看完&#xff0c;arm-aarch64基本上心里有底了&#xff0c;讲的蛮好的 https://xz.aliyun.com/t/3154 一些汇编指令和示例 https://zhuanlan.zhihu.com/p/673591189 https://liujiaboy.github.io/2021/04/13/%E9%80%86%E5%90%91/ARM%E6%B1%87%E…

【2024系统架构设计】案例分析- 1软件架构设计

目录 一 基础知识 二 历年真题 案例分析前文回顾: 【2024系统架构设计】 系统架构设计师第二版-大数据架构理论设计与实践 【2024系统架构设计】 系统架构设计师第二版-云原生架构设计理论与实践

Linux下软件安装的命令【RPM,YUM】及常用服务安装【JDK,Tomcat,MySQL】

Linux下软件安装的命令 源码安装 以源代码安装软件&#xff0c;每次都需要配置操作系统、配置编译参数、实际编译&#xff0c;最后还要依据个人喜好的方式来安装软件。这个过程很麻烦很累人。 RPM软件包管理 RPM安装软件的默认路径: 注意&#xff1a; /etc 配置文件放置目录…

《Python数据分析技术栈》第06章使用 Pandas 准备数据 11 pandas中的运算符 Operators in Pandas

11 pandas中的运算符 Operators in Pandas 《Python数据分析技术栈》第06章使用 Pandas 准备数据 11 pandas中的运算符 Operators in Pandas Pandas uses the following operators that can be applied to a whole series. While Python would require a loop to iterate thr…

VsCode容器开发 - VsCode连接远程服务器上的docker

VsCode容器开发 - VsCode连接远程服务器上的docker 前言 之前在服务器上的Docker内开发&#xff0c;文件编辑起来就很不爽。不如使用VsCode直接打开远程服务器上的Docker&#xff0c;这样就能在VsCode里直接无缝编辑Docker里的文件了。 但是百度和必应得到的中文结果都很奇葩…