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

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

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

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

  1. 检测是否安装
  2. 下载安装包
  3. 解压安装包并安装
  4. 运行

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

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

  • 从A地址下载安装包失败后从B地址下载
  • 从B地址下载安装包失败后从C地址下载
  • 从C地址下载安装包失败后认为执行失败
  • 下载成功后,检测CPU是否繁忙
  • CPU繁忙则继续检测CPU是否繁忙
  • CPU不繁忙则执行解压
  • 解压失败则重新下载。如果之前后A地址下载,则本次从B地址下载;如果之前从B地址下载,则本次从C地址下载
  • 解压成功后执行
  • 运行失败则重新下载。如果之前后A地址下载,则本次从B地址下载;如果之前从B地址下载,则本次从C地址下载
  • 运行成功则认为执行成功

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

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

我们以状态图来表示:

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

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

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

/* 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_FAI
class 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()
};

这个类也非常简单,它对应于图中的


        其中REGISTERSTATECONVERTBEGIN宏指定了该复合状态的起始状态(状态类),REGISTERSTATECONVERT指定了状态翻转逻辑(前状态类,条件,后状态类)

我们再看下“安装后运行状态”这个组合状态的类

#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<CCollectionState_Install_Run, CMachine_Download_Run_App, CStoreofMachine>
{
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()
};

该类的写法也很简单REGISTERSTATECONVERTBEGIN、REGISTERSTATECONVERT和REGISTERSTATECONVERTEND三个宏构成了整个状态跳转图


        最后我们再看下状态机的类

#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<CMachine_Download_Run_App, CStoreofMachine>
{
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()
};

它也是通过三个宏构成了整个逻辑跳转。

在模块独立的前提下,该状态机还算是比较优雅简洁的展现了整个状态跳转的流程。当然在这个简洁的背后还是隐藏了很多背后的秘密。我们将在下节介绍其实现。

我们最终通过如下代码,让整个状态机运行起来:

    boost::shared_ptr<CMachine_Download_Run_App> spc = boost::make_shared<CMachine_Download_Run_App>();spc->StartMachine();

使用C++实现一套简单的状态机模型——实例相关推荐

  1. 使用C++实现一套简单的状态机模型——原理解析

    在上一文中,我们介绍了该状态机模型的使用方法.通过例子,我们发现可以使用该模型快速构建满足基本业务需求的状态机.本文我们将解析该模型的基础代码,以便大家可以根据自己状态机特点进行修改.(转载请指明出于 ...

  2. 一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:huanzi-qch cnblogs.com/huanzi- ...

  3. sql 拼接int类型的字段_一套简单实用的SQL脚本(下篇)

    点击上方SQL数据库开发,关注获取SQL视频教程 SQL专栏 SQL数据库基础知识汇总 SQL数据库高级知识汇总 之前已经分享了一部分内容<一套简单实用的SQL脚本(上篇)>接上一章我们继 ...

  4. ajaxfileupload 访问不到后台_一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)...

    前言 这套Base Admin是一套简单通用的后台管理系统,主要功能有:权限管理.菜单管理.用户管理,系统设置.实时日志,实时监控,API加密,以及登录用户修改密码.配置个性菜单等 技术栈 前端:la ...

  5. 搭建一套简单的CDN网络

    目前对于CDN网络搭建技术有很多成熟的商业方案,对于资金受限的网站可能无法自己搭建或购买.这里提供了一套简单的实现CDN网络的技术架构,所使用的软件全部是开源高效且免费的. 根据CDN网络技术原理,必 ...

  6. 设计一套简单的计算机系统及其指令系统,【精品】计算机组成综合设计指导书...

    [精品]计算机组成综合设计指导书 (45页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 <计算机组成综合设计>指导董志学刘志强 ...

  7. 一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目源码)

    前言 这套Base Admin是一套简单通用的后台管理系统,主要功能有:权限管理.菜单管理.用户管理,系统设置.实时日志,实时监控,API加密,以及登录用户修改密码.配置个性菜单等 技术栈 前端:la ...

  8. C语言实现简单的状态机

    先来看一下状态机的定义: 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作.完成特定操作的控制中心.有限状态机简写为FSM(Finite Sta ...

  9. 状态模式的介绍及状态机模型的函数库javascript-state-machine的用法和源码解析

    文章大体就两部分: 状态模式 状态机模型的函数库javascript-state-machine的用法和源码解析 场景及问题背景: 我们平时开发时本质上就是对应用程序的各种状态进行切换并作出相应处理. ...

最新文章

  1. 南京晓庄学院大一第二学期计算机数据结构期末考试试卷及答案,南京晓庄学院数据结构题库参考答案.docx...
  2. 【廖雪峰python入门笔记】Unicode编码_UnicodeDecodeError处理
  3. MASH结构相噪分析的simulink仿真
  4. 如何在本地连接服务器上的MySQL
  5. html 上传文件_【实战篇】记一次文件上传漏洞绕过
  6. 为什么 Git把 SVN拍在了沙滩上!
  7. 看清美国“黑客帝国”的真面目
  8. IDEA 导入Weka的Maven依赖jar包
  9. 自增主键为什么不是连续的?
  10. sendEmail invalid SSL_version at SSL.pm
  11. 部署VC2008应用程序时不能运行解决办法
  12. HTTP协议之chunk介绍
  13. 在Ubuntu 20.04上面搭建嵌入式开发环境
  14. 【架构师技能篇】Worker初识
  15. C++优化之使用emplace、emplace_back
  16. 数据分析工具集合:Tableau入门及其他工具简介
  17. 英语语法(2)----点破主谓宾系表三大句型
  18. TOY++美私奢玩:TEAx正茶链盟 +雅贡臻品登陆台北!跨界斜杠派展
  19. 小米电视android刷机,小米电视怎么刷机?小米电视刷第三方系统固件下载
  20. CV2 图像归一化函数

热门文章

  1. Open3D KdTree建立、3种近邻搜索及结果可视化
  2. Docker容器化部署config-server无法直接访问
  3. 【opencv】(2) 图像处理:边界填充、图像融合、图像阈值、数值计算
  4. android列表实现置顶,Android利用RecyclerView实现全选、置顶和拖拽功能示例
  5. linux笔记软件,Linux Ubuntu学习笔记_软件管理
  6. java程序员可以只用windos吗_程序员要写多少代码 才能开发一个window操作系统
  7. java保存多张图片格式_从多个URL下载多个图像文件并保存到本地计算机(使用R)...
  8. 基于Sobel计算图像梯度图
  9. C++中关于[]静态数组和new分配的动态数组的区别分析
  10. Unity电子游戏优化终极指南 The Ultimate Guide to Video Game Optimisation