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

news2024/11/15 17:19:25

状态模式

如何去描述状态机?

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

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.coloradmin.cn/o/1403816.html

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

相关文章

windows用mingw(g++)编译opencv,opencv_contrib,并install安装

windows下用mingw编译opencv貌似不支持cuda&#xff0c;选cuda会报错&#xff0c;我无法解决&#xff0c;所以没选cuda&#xff0c;下面两种编译方式支持。 如要用msvc编译opencv&#xff0c;参考我另外一篇文章 https://blog.csdn.net/weixin_44733606/article/details/1357…

gRPC-gateway使用介绍

gRPC-gateway 参考资料&#xff1a;gRPC-Gateway使用指南 服务中&#xff0c;使用了gRPC gateway&#xff08;代理&#xff09;来将外部的http请求映射为内部rpc调用。 proto文件示例&#xff1a; // 导入google/api/annotations.proto import "google/api/annotations…

《WebKit 技术内幕》学习之九(4): JavaScript引擎

4 实践——高效的JavaScript代码 4.1 编程方式 关于如何使用JavaScript语言来编写高效的代码&#xff0c;有很多铺天盖地的经验分享&#xff0c;以及很多特别好的建议&#xff0c;读者可以搜索相关的词条&#xff0c;就能获得一些你可能需要的结果。同时&#xff0c;本节希望…

在全志H616核桃派上实现USB摄像头的OpenCV颜色检测

在给核桃派开发板用OpenCV读取图像并显示到pyqt5的窗口上并加入颜色检测功能&#xff0c;尝试将图像中所有蓝色的东西都用一个框标记出来。 颜色检测核心api 按照惯例&#xff0c;先要介绍一下opencv中常用的hsv像素格式。颜色还是那个颜色&#xff0c;只是描述颜色用的参数变…

图神经网络X项目|基于图神经网络的电商行为的预测(5%)

文章目录 Jupyter Notebook 学习人工智能的好帮手数据集数据集下载数据集调用数据集应用技巧——获取不重复的编号数据集应用技巧——随机采样数据集应用技巧——抽取前N项进行模拟测试 数据集构建技巧一——查看数据集构建进度 Jupyter Notebook 学习人工智能的好帮手 【Jupy…

opencv010 卷积02(方盒滤波和均值滤波)

今天继续学习滤波器的相关知识&#xff01;这篇比较简单&#xff0c;也短一些&#xff0c;明天写高斯滤波 方盒滤波 boxFilter(scr, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) 方盒滤波的卷积核如下&#xff1a; normalize&#xff08;标准化&#xff0…

从潮汐架构和安第斯大模型,看智能手机的未来演进

好久没聊手机了&#xff0c;今天聊聊手机。 最近这段时间&#xff0c;手机厂商纷纷发布了自家最新的旗舰系列。其中&#xff0c;有一些技术&#xff0c;蛮值得关注的。 大家都知道&#xff0c;手机行业是出了名的“内卷”&#xff0c;厂商之间的竞争非常激烈。但从本质来说&…

STL之unordered_map使用方法

这里写目录标题 STL之unordered_map使用方法1.什么是STL呢2.unordered_map2.1 头文件&#xff1a;2.2 怎么创建&#xff1a;2.3 初始化&#xff1a;2.4 根据key获取对应value值&#xff1a;2.5 遍历&#xff0c;判断key是否存在&#xff1a;2.6 怎么根据迭代器it获取key和value…

浅谈拨测在网络安全中的应用

在当今数字化时代&#xff0c;网络安全成为各个行业和组织关注的焦点。为了保障网络的稳定性和信息的安全&#xff0c;拨测安全性成为一种日益重要的工具。本文将介绍拨测在网络安全中的应用&#xff1a; 1.威胁模拟 通过威胁模拟&#xff0c;拨测安全性可以模拟各种网络攻击&a…

分布式websocket IM聊天系统相关问题问答【第九期】

前言 上期视频讲解了自己关于聊天系统的设计的时候出现了一些不一样的声音。不了解情况的可以看上上期视频。这期主要是讨论。IM聊天系统设计方案多。我的先说明一下自己的技术背景互相之间才能更好的理解。 本期对应视频 目前已经写的文章有。并且有对应视频版本。 git项目地…

小白初探架构模式—常用的设计模式

目录 1.前言 2. 主从架构 2.1 主从架构的优点 2.2 主从架构的应用场景 2.3 主从架构的实现 2.4 主从架构的示例 3. 主从架构设计的延伸 3.1 主备模式 3.2 主从复制 3.3 集群分片 3.4 异地多活 4. 总结 1.前言 作为一个架构设计小白&#xff0c;我们通常用了很多种工具&…

Java和Redis实现一个简单的热搜功能

1. 前言 我们有一个简单的需求&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录。用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该字符被搜索的个数以及当前的时间戳 &#xff08;用…

4_机械臂运动学基础向量空间

在了解机械臂正解推导的过程中&#xff0c;几个问题一直困扰着我&#xff1a; 1、为什么3*3矩阵可以描述姿态&#xff1f;矩阵更进一步的意义是什么&#xff1f;姿态是否有其他的描述方式&#xff0c;如果有是什么&#xff1f; 2、机械臂法兰中心相对于基座的坐标&#xff0c;6…

开始学习vue2基础篇(初体验)

一、什么是VUE&#xff08;官网 &#xff1a;https://cn.vuejs.org/&#xff09; 官方给出的概念 &#xff1a;Vue (读音 /vju ː/ &#xff0c;类似于 view) 是一套用 于构建用户界面的前端框架 渐进式的 JavaScript 框架 二、VUE的特点 易用 &#xff1a;基础只需HTML、CSS、…

[小程序]页面事件

一、下拉刷新 1.开启和配置 小程序中开启下拉刷新的方式有两种&#xff1a; ①全局开启下来刷新 在app.json的window节点中&#xff0c;设置enablePullDownRefresh设为ture。 ②局部开启下来刷新 在页面对应的json文件的的window节点中&#xff0c;设置enablePullDownRefresh设…

yolov5 opencv dnn部署 github代码

yolov5 opencv dnn部署 github代码 源码地址实现推理源码中作者的yolov5s.onnx推理条件python部署(因为python比较简单就直接介绍了)c部署 参考链接 源码地址 yolov5官网还提供的dnn、tensorrt推理链接本人使用的opencv c github代码,代码作者非本人&#xff0c;也是上面作者推…

定向减免!函数计算让轻量 ETL 数据加工更简单,更省钱

作者&#xff1a;澈尔、墨飏 业内较为常见的高频短时 ETL 数据加工场景&#xff0c;即频率高时延短&#xff0c;一般均可归类为调用密集型场景。此场景有着高并发、海量调用的特性&#xff0c;往往会产生高额的计算费用&#xff0c;而业内推荐方案一般为攒批处理&#xff0c;业…

【EI会议征稿通知】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…

不建Vivado工程,也能看Device视图

不建Vivado工程&#xff0c;也能看Device视图 在FPGA设计与开发中&#xff0c;Device视图和Package视图发挥着重要的作用。 在Device视图下&#xff1a; 可以查看FPGA芯片可用资源 例如&#xff1a;LUT、FF、BRAM、DSP、URAM等的个数&#xff1b; 可以查看关键资源的分布情…

搭建redis服务器

memcached MongoDB Redis 先把数据存储在内存里,如何定期把内存里数据存储在硬盘,一个Key一个Values redis集群存储数据在内存里面 mysql集群存储数据在硬盘里 netstat -utnlp | grep redis-server 查看端口tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 1970/redis-server 1 …