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

状态模式

如何去描述状态机?

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

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博客​​​​​​​

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习实验1——朴素贝叶斯和逻辑回归分类Adult数据集

文章目录 &#x1f9e1;&#x1f9e1;实验内容&#x1f9e1;&#x1f9e1;&#x1f9e1;&#x1f9e1;数据预处理&#x1f9e1;&#x1f9e1;认识数据填充缺失值&#xff08;“ &#xff1f;”&#xff09;将income属性替换为0-1变量筛除无效属性编码和缩放 &#x1f9e1;&…

GC6208 5V摄像机镜头驱动芯片,为什么可以替代AN41908,适用于摄像机镜头上

GC6208是一个镜头电机驱动IC摄像机和安全摄像机。该装置集成了一个由PID控制的可变光圈直流电机驱动器和两个通道的扫描隧道显微镜电机驱动器&#xff0c;用于变焦和聚焦控制。AN41908A是一款用于摄像机和安全摄像机的镜头马达驱动IC&#xff0c;具有lris控制功能。电压驱动系统…

基于springboot+vue新能源汽车充电管理系统

摘要 新能源汽车充电管理系统是基于Spring Boot和Vue.js技术栈构建的一款先进而高效的系统&#xff0c;旨在满足不断增长的新能源汽车市场对充电服务的需求。该系统通过整合前后端技术&#xff0c;实现了用户注册、充电桩管理、充电订单管理等核心功能&#xff0c;为用户提供便…

bxCAN 工作模式

bxCAN 工作模式 bxCAN 有三种主要的工作模式&#xff1a;初始化、正常和睡眠。硬件复位后&#xff0c;bxCAN 进入睡眠模式以降低功耗&#xff0c;同时 CANTX 上的内部上拉电阻激活。软件将主控制寄存器&#xff08;CAN_MCR---CAN master control register&#xff09;的初始化…

HTML+CSS:3D轮播卡片

效果演示 实现了一个3D翻转的卡片动画&#xff0c;其中每个卡片都有不同的图片和不同的旋转角度。整个动画循环播放&#xff0c;无限次。整个页面的背景是一个占据整个屏幕的背景图片&#xff0c;并且页面内容被隐藏在背景图片之下。 Code <div class"container"…

高效构建Java应用:Maven的使用总结

一、Maven简介和快速入门 1.1 Maven介绍 Maven-Introduction Maven 是一款为 Java 项目构建管理、依赖管理的工具&#xff08;软件&#xff09;&#xff0c;使用 Maven 可以自动化构建、测试、打包和发布项目&#xff0c;大大提高了开发效率和质量。 总结&#xff1a;Maven…

09-微服务Sentinel整合GateWay

一、概述 在微服务系统中&#xff0c;网关提供了微服务系统的统一入口&#xff0c;所以我们在做限流的时候&#xff0c;肯定是要在网关层面做一个流量的控制&#xff0c;Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。 1.1 总览 Sentinel 1.6.…

在线英文字母大小写转换工具

在线英文字母大小写转换 - BTool在线工具软件&#xff0c;为开发者提供方便。在线快速转换一段英文内容的大小写格式&#xff0c;例如转为一般句子大小写、全部小写、全部大写、大小写交错或像是标题的首字大写等等格式。https://www.btool.cn/case-converter此工具可在线快速转…

【书生·浦语】大模型实战营——第六次作业

使用OpenCompass 评测 InterLM2-chat-chat-7B 模型在C-Eval数据集上的性能 环境配置 1. 创建虚拟环境 conda create --name opencompass --clone/root/share/conda_envs/internlm-base source activate opencompass git clone https://github.com/open-compass/opencompass cd…

【Redis数据类型】String实现及应用场景

文章目录 String1、介绍2、内部实现整数值embstr 编码字符串raw编码字符串 3、常用命令4、应用场景缓存对象常规计数分布式锁共享session信息 参考&#xff1a;小林Coding Redis九种数据类型 Redis 提供了丰富的数据类型&#xff0c;常见的有五种&#xff1a;String&#xff08…

C++ 之LeetCode刷题记录(十四)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 依旧是追求耗时0s的一天。 88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &…

打造出色的 Prometheus 监控系统,看完后薪资翻倍?

一、监控概念&误区 监控是管理基础设施和业务的核心工具&#xff0c;监控应该和应用程序一起构建和部署&#xff0c;没有监控&#xff0c;将无法了解你的系统运行环境&#xff0c;进行故障诊断&#xff0c;也无法阻止提供系统性的性能、成本和状态等信息。 误区&#xff1…

怎样的安全数据交换系统 可以支持信创环境?

首先&#xff0c;我来看看&#xff0c;什么是安全数据交换系统&#xff1f;安全数据交换系统是一种专门设计用于在不同网络环境之间安全传输数据的技术解决方案。它确保数据在传输过程中的完整性、机密性和可用性&#xff0c;同时遵守相关的数据保护法规和行业标准。 那么&…

软件设计师——法律法规(四)

&#x1f4d1;前言 本文主要是【法律法规】——软件设计师——法律法规的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

为什么MOS管很容易失效?有哪些失效?

在电子元件中&#xff0c;金属-氧化物半导体场效应晶体管&#xff08;MOS管&#xff09;是独特且重要&#xff0c;然而相比其他元件&#xff0c;MOS管很容易失效&#xff0c;导致电路无法正常运行&#xff0c;因此工程师必须查找原因并解决问题。 1、MOS管为什么很容易失效&…

Ubuntu之离线安装Gitlab,搭建私有代码仓库

Ubuntu之离线安装Gitlab,搭建私有代码仓库 文章目录 Ubuntu之离线安装Gitlab,搭建私有代码仓库1. 官网下载&#xff1a;2. 安装Gitlab3. 使用 1. 官网下载&#xff1a; https://packages.gitlab.com/gitlab/gitlab-ce wget下载地址&#xff1a; wget https://packages.gitla…

立体视觉几何 (二)

1.视差 2.立体匹配 立体匹配的基本概念: 匹配目标: 在立体匹配中&#xff0c;主要目标是确定左图像中像素的右图像中的对应像素。这个对应像素通常位于相同的行。视差&#xff08;Disparity&#xff09;: 视差 d 是右图像中对应像素 xr 和左图像中像素 xl 之间的水平位置差。视…

go 语言中 json.Unmarshal([]byte(jsonbuff), j) 字节切片得使用场景

struct_tag的使用 在上面的例子看到&#xff0c;我们根据结构体生成的json的key都是大写的&#xff0c;因为结构体名字在go语言中不大写的话&#xff0c;又没有访问权限&#xff0c;这种问题会影响到我们对json的key的名字&#xff0c;所以go官方给出了struct_tag的方法去修改…

跑步运动耳机哪个牌子好?2024年国产运动耳机推荐

​无论春夏秋冬&#xff0c;无论室内还是户外&#xff0c;运动都能带给我们无尽的乐趣。而一副好的运动耳机&#xff0c;更能为我们的运动体验增色不少。今天&#xff0c;就让我为大家推荐几款值得一试的运动耳机吧。 1.南卡开放式耳机&#xff08;00压&#xff09; 一句话评价…

持续集成工具Jenkins的使用之安装篇(一)

Jenkins是一个基于Java开发的开源的一种持续集成工具&#xff0c;主要用于环境部署&#xff0c;监控重复性的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。要想使用它&#xff0c;你就必须的先安装&#xff0c;接下来我们就介绍下J…