设计模式之状态机模式

  Hello,笔者最近工作被领导要求写了一个状态机,说实在的,笔者之前从来没有写过状态机(越做工作越发现自己越菜),所以不得已找了一些网上的一些资料,发现其实状态机也有特定的设计模式的,所以我想针对我这个项目来聊一聊状态机这个事情。说实在的,这个事情看起来不太大,但是里面要注意的东西真心不少,因此,笔者决定分成两篇博客来写,这篇博客主要针对状态模式以及我的项目来初步的了解状态机怎么写,之后的文章就聊一聊项目中应该注意的点以及状态机写的时候应该注意些什么。

  一、状态机是什么

  二、项目背景大致说明

  三、状态机设计思想和图

  四、状态模式和策略模式的区别

  一、状态机是什么?

    所谓状态机,就是当一个对象状态转换的条件表达式过于复杂的时候,把状态的判断逻辑转换到不同状态的一系列类当中去。这样解释可能有点抽象,我们举一个简单的例子,我们以电梯为例,电梯可以分成开门,关门,上升/下落,停止这五个部分。首先我们要明确两点,就是首先这五种状态在同一时间只能出现一个,其次,这五种状态在满足某种条件后是可以相互转换的,比如下落到某楼层后就会进入停止状态,那么这也是状态机使用的两个前提,第一,在某段时间内只准许出现一种状态,第二,这些状态在满足某些条件后是可以相互转换的。

  二、项目背景大致说明

    这个项目背景大概是这样的,当前我们有四个状态,分别为IDLE状态,PREPARE状态,PROCESSING状态,POSTPONE状态,其中每种状态的切换依赖于两种条件(因为这里涉及到工作隐私,所以就不写那么详细了),另外,PREPARE状态,PROCESSING状态,POSTPONE状态如果持续一段时间,那么他们就会退回IDLE状态,大致情况就是这样,详情请看图片。

 三、状态机的设计思想和类图

  我的设计思想是这样的:首先整体分成六个类,我们姑且把这些类称为stateManager,IDLE,PREPARE,PROCESSING以及POSTPONE以及FSMInterface类。其中stateManager类作为管理类,分别管理状态的迁移以及传输判断条件,而FSMInterface类作为接口类,作为IDLE,PREPARE,PROCESSING和POSTPONE的父类。其具体的类图如图所示。

 我们从做向右看这个类图,SensorManager中的sensorstate,infraredstate,curIndex,startFlag这四个成员变量都是判断条件所依赖的全局变量,s则表示当前所处的状态,一旦有条件改变状态时,我们只要调用setstate函数就可以改变函数了。另外我们来看SensorManager中的调用函数handle函数,handle函数用来调用state中的handle,利用多态的方法来调用状态机的处理函数。它的时序图如图所示。

其代码如图所示:

  //  外部条件改变stateManager的全局变量

class stateManager
{
private:
    int m_sensorState;
    int curIndex;
    int startFlag;
    // std::vector<uint32_t> m_infraredState;
public:
    void setStartFlag(int value);
    int  getStartFlag();
    void setSensorState(int state);
    void setInfraredState(uint32_t index, int state);
    bool getSensorState();
    stateManager();
    virtual ~stateManager();
    void setState(State *value);
    void handle();
    void setCurIndex(int value);
    int getCurIndex();
    std::shared_ptr<State> current;   //当前状态
    std::map<uint32_t, int> m_infraredState;
};
void stateManager::handle()
{
    current->handle(this);
}
void Idle::handle(stateManager *m)
{
    // init
    m->setSensorState(false);
    m->setCurIndex(-1);
    m->m_infraredState.clear();
    // init end
    LOG_INFO("Idle::handle is {0},signalState is {1}", __LINE__, IDLENAME);
    emit signalState(IDLENAME);
    // sensor changed
    if (m->getSensorState() == 1) {
        LOG_INFO("------the sensormanager set up,Idle::handle is {0}", __LINE__);
        DefaultClient::getInstance()->getRfidReaderManager()->startInventory((DOORLOGICID));
        m->setStartFlag(1);
        m->current = std::make_shared<Prepare>();
        m->handle();
    }

    // infrared changed
    int flag = 0;
    for (auto iter : m->m_infraredState) {
        if (iter.second == 1) {
            m->setCurIndex(iter.first);
            flag = 1;
            break;
        }
    }

    if (flag == 1) {
        DefaultClient::getInstance()->getRfidReaderManager()->startInventory((DOORLOGICID));
        m->setStartFlag(1);
        m->current = std::make_shared<Processing>();
        m->handle();
    }
}

我们重点关注一下stateManager::handle()函数,当每一次有状态切换时,我们调用这个函数,在这个函数,我们找到当前current指针,调用相应的handle函数,调用的同时我们把this指针传进去(因为我们要根据里面的变量限定条件)
。之后再IDLE::HANDLE内部中,一旦状态改变,我们就可以修改current的值了。这也是设计模式中状态模式的基本用法。

  四、策略模式和状态模式的总结

    在设计模式当中,还有一种模式和状态模式很像,就是策略模式。两者的共同点在于都是由一个管理类来完成总结,都是以类来传递。但是策略模式一般来说是在初始化的时候就决定好了算法,而状态模式是在运行过程中出现的,两者
    是不同的。

  总结:这个算法其实是一个最简单的状态机,这个代码我之后被上司强行改了5遍,至于具体的原因我会在下一篇博客中做说明。
















    

原文地址:https://www.cnblogs.com/songyuchen/p/14094207.html