一般来说,“状态机”是一种表达状态转换变换逻辑的方法。曾经有人和我讨论过为什么不直接用ifelse,而要使用“状态机”去实现一些逻辑,认为使用“状态机”是一种炫技的表现。然而对于大型复杂逻辑的变化和跳转,使用ifelse将带来代码难以阅读等弊端。其实ifelse也是一种状态机实现的方式。
之前我们有个业务和操作系统有着强烈的关联,而我们希望比较清晰地描述整个业务中各个子业务的过程,就引入了状态机描述的方式。可是当时的状态机是使用if else方法描述,显得整个过程比较臃肿,阅读起来也不够清晰。于是我尝试引入第三方的状态机库来重构这块的业务——比如boost里的状态机库。可是使用过程中感觉到了很多不便,索性自己动手实现一套清晰优雅的状态机模型。(转载请指明出于breaksoftware的csdn博客)
编写模型之前,我们需要了解什么是状态机。我在搜索引擎上搜索到了若干结果,但是大部分都显得非常学术化。而实现一个大而全、包罗万象、放之四海而皆适宜的状态机模型也并非我的设计初衷。我设计的状态机具有如下特性:单线程、浅历史。单线程即我们的状态机是在一个线程内部运行的,不受外界其他线程干扰,这样我们在设计时就不用考虑多线程编程的问题。浅历史是状态机中的一个概念,它是指只记录最高一层复合状态的最后离开状态。这个特性如果有不了解的,可以先去搜索下。在实践中,该特性还是非常有用的。
我们以一个简单、可能不恰当的例子来引入我这个状态机。我们先设计一个应用场景:给用户电脑安装软件并运行。这个场景我们可以拆分为如下几个逻辑:
检测是否安装下载安装包解压安装包并安装运行
这四个逻辑并不复杂,我们将其定义为基础状态——一种可以持续一段时间且内部执行逻辑我们不关心的状态。为了让这个逻辑变得稍微有点复杂,我们设计如下要求:
对于未安装该软件的情况:
从A地址下载安装包失败后从B地址下载从B地址下载安装包失败后从C地址下载从C地址下载安装包失败后认为执行失败下载成功后,检测CPU是否繁忙
我们以状态图来表示:
图中“下载复合状态”是一个具有浅历史特性的复合状态;“安装后运行状态”是一个状态组合集,它让一组复杂的状态转换关系缩变成一种状态。这样如果其他地方需要复用该组合时,只要引入该组合状态名即可。<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ICAgICAgICDO0sPHtNO4w8Sj0M3KudPD1d+1xL3HtsjIpb+0yOe6zsilyei8xrrNseDQtLT6wuujrNbB09q0+sLr1tC1xMSjsOW6zbqvyv2/ydLUz8i69sLUtfSjrM7Sw8fPyMHLveLG5LTzuMXKudPDoaM8L3A+CjxwPiAgICAgICAgtNPJz8281tDO0sPHv8nS1Mi3tqjT0Mjnz8LK5LP2zPW8/jwvcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">/* 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 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#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 {
public :
CSimpleState_Download_From_A( void ) {};
~CSimpleState_Download_From_A( void ){};
public :
void Entry() {
};
std::string Exit() {
return CONDITION_DOWNLOAD_SUC;
};
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#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_FAI
class CCompositeState_Download:
public AutoStateChart::CCompositeStates {
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()
};
|
其中REGISTERSTATECONVERTBEGIN宏指定了该复合状态的起始状态(状态类),REGISTERSTATECONVERT指定了状态翻转逻辑(前状态类,条件,后状态类)。
我们再看下“安装后运行状态”这个组合状态的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#pragma once
#include "AutoStateChart.h"
#include "StoreMachine.h"
// 引入输出宏
#include "CondDefine.h"
// 引入子状态
#include "CompositeState_Download.h"
#include "SimpleState_Failed.h"
#include "SimpleState_CheckCPU.h"
#include "SimpleState_Unzip.h"
#include "SimpleState_Install.h"
#include "SimpleState_Run.h"
#include "SimpleState_Uninstall.h"
#include "SimpleState_Success.h"
class CMachine_Download_Run_App; // 前置申明状态机类
class CCollectionState_Install_Run:
public AutoStateChart::CCollectionStates {
public :
CCollectionState_Install_Run( void ){};
~CCollectionState_Install_Run( void ){};
public :
REGISTERSTATECONVERTBEGIN(CCompositeState_Download)
REGISTERSTATECONVERT(CCompositeState_Download, CONDITION_DOWNLOAD_SUC, CSimpleState_CheckCPU)
REGISTERSTATECONVERT(CCompositeState_Download, CONDITION_DOWNLOAD_FAI, CSimpleState_Failed)
REGISTERSTATECONVERT(CSimpleState_CheckCPU, CONDITION_BUSY, CSimpleState_CheckCPU)
REGISTERSTATECONVERT(CSimpleState_CheckCPU, CONDITION_NOBUSY, CSimpleState_Unzip)
REGISTERSTATECONVERT(CSimpleState_Unzip, CONIDTION_UNZIP_SUC, CSimpleState_Install)
REGISTERSTATECONVERT(CSimpleState_Unzip, CONDITION_UNZIP_FAI, CCompositeState_Download)
REGISTERSTATECONVERT(CSimpleState_Install, CONDITION_INSTALL_SUC, CSimpleState_Run)
REGISTERSTATECONVERT(CSimpleState_Install, CONDITION_INSTALL_FAI, CCompositeState_Download)
REGISTERSTATECONVERT(CSimpleState_Run, CONDITION_RUN_SUC, CSimpleState_Success)
REGISTERSTATECONVERT(CSimpleState_Run, CONDITION_RUN_FAI, CSimpleState_Uninstall)
REGISTERSTATECONVERT(CSimpleState_Uninstall, "" , CCompositeState_Download)
REGISTERSTATECONVERTEND()
};
|
最后我们再看下状态机的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#pragma once
#include "AutoStateChart.h"
#include "StoreMachine.h"
// 引入输出宏
#include "CondDefine.h"
// 引入子状态
#include "SimpleState_CheckExist.h"
#include "CollectionState_Install_Run.h"
#include "SimpleState_Run.h"
#include "SimpleState_Uninstall.h"
#include "SimpleState_Success.h"
class CMachine_Download_Run_App :
public AutoStateChart::CAutoStateChartMachine {
public :
CMachine_Download_Run_App( void ) {};
~CMachine_Download_Run_App( void ) {};
public :
REGISTERSTATECONVERTBEGIN(CSimpleState_CheckExist)
REGISTERSTATECONVERT(CSimpleState_CheckExist, CONDITION_NOEXIST, CCollectionState_Install_Run)
REGISTERSTATECONVERT(CSimpleState_CheckExist, CONDITION_EXIST, CSimpleState_Run)
REGISTERSTATECONVERT(CSimpleState_Run, CONDITION_RUN_FAI, CSimpleState_Uninstall)
REGISTERSTATECONVERT(CSimpleState_Run, CONDITION_RUN_SUC, CSimpleState_Success)
REGISTERSTATECONVERT(CSimpleState_Uninstall, "" , CCollectionState_Install_Run)
REGISTERSTATECONVERTEND()
};
|
在模块独立的前提下,该状态机还算是比较优雅简洁的展现了整个状态跳转的流程。当然在这个简洁的背后还是隐藏了很多背后的秘密。我们将在下节介绍其实现。
我们最终通过如下代码,让整个状态机运行起来:
1
2
|
boost::shared_ptr
spc->StartMachine();
|