使用C++实现一套简单的状态机模型

一般来说,“状态机”是一种表达状态转换变换逻辑的方法。曾经有人和我讨论过为什么不直接用ifelse,而要使用“状态机”去实现一些逻辑,认为使用“状态机”是一种炫技的表现。然而对于大型复杂逻辑的变化和跳转,使用ifelse将带来代码难以阅读等弊端。其实ifelse也是一种状态机实现的方式。

之前我们有个业务和操作系统有着强烈的关联,而我们希望比较清晰地描述整个业务中各个子业务的过程,就引入了状态机描述的方式。可是当时的状态机是使用if else方法描述,显得整个过程比较臃肿,阅读起来也不够清晰。于是我尝试引入第三方的状态机库来重构这块的业务——比如boost里的状态机库。可是使用过程中感觉到了很多不便,索性自己动手实现一套清晰优雅的状态机模型。(转载请指明出于breaksoftware的csdn博客)

编写模型之前,我们需要了解什么是状态机。我在搜索引擎上搜索到了若干结果,但是大部分都显得非常学术化。而实现一个大而全、包罗万象、放之四海而皆适宜的状态机模型也并非我的设计初衷。我设计的状态机具有如下特性:单线程、浅历史。单线程即我们的状态机是在一个线程内部运行的,不受外界其他线程干扰,这样我们在设计时就不用考虑多线程编程的问题。浅历史是状态机中的一个概念,它是指只记录最高一层复合状态的最后离开状态。这个特性如果有不了解的,可以先去搜索下。在实践中,该特性还是非常有用的。

我们以一个简单、可能不恰当的例子来引入我这个状态机。我们先设计一个应用场景:给用户电脑安装软件并运行。这个场景我们可以拆分为如下几个逻辑:

这四个逻辑并不复杂,我们将其定义为基础状态——一种可以持续一段时间且内部执行逻辑我们不关心的状态。为了让这个逻辑变得稍微有点复杂,我们设计如下要求:

对于未安装该软件的情况:

下载成功后,,检测CPU是否繁忙

对于已安装该软件的情况:

运行失败则先进行卸载,然后进入“未安装该软件”逻辑运行成功则认为执行成功

我们以状态图来表示:

图中“下载复合状态”是一个具有浅历史特性的复合状态;“安装后运行状态”是一个状态组合集,它让一组复杂的状态转换关系缩变成一种状态。这样如果其他地方需要复用该组合时,只要引入该组合状态名即可。

我们从该模型使用者的角度去看如何去设计和编写代码,至于代码中的模板和函数可以先忽略掉,我们先了解其大概使用。

从上图中我们可以确定有如下输出条件

/* CondDefine.h*/#pragma once// 是否安装#define CONDITION_EXIST "CONDITION_EXIST"#define CONDITION_NOEXIST "CONDITION_NOEXIST"// 下载是否成功#define CONDITION_DOWNLOAD_SUC "CONDITION_DONWLOAD_SUC"#define CONDITION_DOWNLOAD_FAI "CONDITION_DONWLOAD_FAI"// CPU是否繁忙#define CONDITION_BUSY "CONDITION_BUSY"#define CONDITION_NOBUSY "CONDITION_NOBUSY"// 解压是否成功#define CONIDTION_UNZIP_SUC "CONIDTION_UNZIP_SUC"#define CONDITION_UNZIP_FAI "CONDITION_UNZIP_FAI"// 运行是否成功#define CONDITION_RUN_SUC "CONDITION_RUN_SUC"#define CONDITION_RUN_FAI "CONDITION_RUN_FAI"// 安装是否成功#define CONDITION_INSTALL_SUC "CONDITION_INSTALL_SUC"#define CONDITION_INSTALL_FAI "CONDITION_INSTALL_FAI"

整个状态跳转具有如下基础状态

基础状态类

检测是否安装CSimpleState_CheckExist

从A地址下载CSimpleState_Download_From_A

从B地址下载CSimpleState_Download_From_B

从C地址下载CSimpleState_Download_From_C

检测CPU占用率CSimpleState_CheckCPU

解压CSimpleState_Unzip

安装CSimpleState_Install

卸载CSimpleState_Uninstall

执行成功CSimpleState_Success

执行失败CSimpleState_Failed

运行CSimpleState_Run

我们以“从A地址下载”为例,查看该状态的基础代码

#pragma once#include "AutoStateChart.h"#include "StoreMachine.h"// 引入输出宏#include "CondDefine.h"// 引入业务基类#include "Business_Random.h"class CMachine_Download_Run_App;// 前置申明状态机类class CSimpleState_Download_From_A :public AutoStateChart::CAutoStateChartBase<CSimpleState_Download_From_A, CMachine_Download_Run_App, CStoreofMachine>{public:CSimpleState_Download_From_A(void) {};~CSimpleState_Download_From_A(void){};public:void Entry() {};std::string Exit() {return CONDITION_DOWNLOAD_SUC;};}; 可以发现,该类非常简单。我们只要用模板申明好类(模板参数:自己、状态机类、存储类),并实现Entry和Exit两个函数就行了。我们再看下下载的复合状态类#pragma once#include "AutoStateChart.h"#include "StoreMachine.h"// 引入输出宏#include "CondDefine.h"// 引入子状态#include "SimpleState_Download_From_A.h"#include "SimpleState_Download_From_B.h"#include "SimpleState_Download_From_C.h"class CMachine_Download_Run_App;// 前置申明状态机类// 该类将产生两种输出CONDITION_DONWLOAD_SUC、CONDITION_DONWLOAD_FAIclass CCompositeState_Download:public AutoStateChart::CCompositeStates<CCompositeState_Download, CMachine_Download_Run_App, CStoreofMachine>{public:CCompositeState_Download(void) {};~CCompositeState_Download(void) {};public:REGISTERSTATECONVERTBEGIN(CSimpleState_Download_From_A)REGISTERSTATECONVERT(CSimpleState_Download_From_A, CONDITION_DOWNLOAD_FAI, CSimpleState_Download_From_B)REGISTERSTATECONVERT(CSimpleState_Download_From_B, CONDITION_DOWNLOAD_FAI, CSimpleState_Download_From_C)REGISTERSTATECONVERTEND()}; 这个类也非常简单,它对应于图中的而消极的人则在每个机会都看到某种忧患。

使用C++实现一套简单的状态机模型

相关文章:

你感兴趣的文章:

标签云: