状态机有哪几类(让你全面了解状态机模式的应用原理)

工作中应用到了状态机,使用过程中发现,如果状态机使用得当,那么就会事半功倍。中间也陆陆续续学习研究了状态机的相关知识。所以,在这里做个总结,同时也分享出来。

本文首先简单介绍状态机的基本知识(建议找专门介绍状态机的书籍进行学习),然后基于十字转门的例子,以迁移表的方式来实现有限状态机的功能,接着再介绍经典的状态机模式,最后重点介绍boost startchart的相关知识点。

boost startchart是boost实现的状态机库,它几乎支持了所有的UML中状态机的特性,主要学习的途径就是官网提供的指南,该指南信息量很大,但是学习起来有点费劲,而且例子也不够完整,所以,本文也会基于它提供的例子,比如hello world、秒表、数码相机,重新梳理总结它的应用方式,至于高级议题,需要以后再花时间进行研究。

一、状态机基本知识

一般状态机由三个元素组成:状态、事件、反应。而反应在boost startchart包括转移、动作等。一个状态可以对应一个或者多个反应。

当前状态收到事件后,执行反应,然后转变为新的状态。该流程会使用下图的方式来表示。

状态机有哪几类(让你全面了解状态机模式的应用原理)(1)

状态机通常都需要有历史状态,可以用来恢复,它分为浅历史和深历史两类。

历史状态是伪状态, 其目的是记住从组合状态中退出时所处的子状态, 当再次进入组合状态时, 可以直接进入子状态, 而不是再从组合状态的初态开始。

浅历史状态, 只记住最外层组合状态的历史,使用大写H来表示。深历史状态, 可以记住任意深度的组合状态的历史,使用大写H和星号组合来表示。

状态机有哪几类(让你全面了解状态机模式的应用原理)(2)

二、迁移表

进出地铁的时候,有时候设置的是一个十字转门,十字转门默认是锁的状态,当投入硬币之后,当前十字转门就会变成解锁状态,当人通过之后,十字转门又会变成锁的状态。当十字转门是锁的状态,但是强行通过,就会发出警告信息。其状态的转换如下图所示。

状态机有哪几类(让你全面了解状态机模式的应用原理)(3)

接下来,我们通过迁移表的方式来说实现上图的状态机图。

首先定义实现动作类接口和实现,即unlock/lock/alarm/thanks。这里定义十字转门的控制接口JTurnstileControlInterface,主要是依据开闭原则,当动作类的功能改变的时候,只需要继承JTurnstileControlInterface接口类,然后重新实现对应的接口函数。

状态机有哪几类(让你全面了解状态机模式的应用原理)(4)

然后定义状态和事件,LOCKED和UNLOCKED表示的是十字转门的状态,COIN和PASS表示的是十字转门收到的事件。

状态机有哪几类(让你全面了解状态机模式的应用原理)(5)

有了上面的基础之后,最后就可以用迁移表的方式来实现十字转门的状态机。

定义十字转门类,构造函数接受动作类,event接收事件,Transition是存储状态转移关系的内部类。

状态机有哪几类(让你全面了解状态机模式的应用原理)(6)

实现十字转门类,event是处理接收到事件的函数,该函数会遍历vector向量中存储的状态迁移表,如果匹配到对应的事件,那么修改当前的状态,并且执行对应的动作。

状态机有哪几类(让你全面了解状态机模式的应用原理)(7)

实现完成十字转门之后,现在就来验证下效果,首先创建十字转门的动作对象指针,将其传入十字转门对象的构造函数,然后调用event函数,传入事件COIN, 执行完成之后,再传入事件PASS,来查看当前动作的执行是否正确。

状态机有哪几类(让你全面了解状态机模式的应用原理)(8)

最后运行程序,输出的信息如下,从中可以看到,接收到COIN事件,执行了unlock动作,接收到PASS事件,执行了lock动作,这个符合预期。

状态机有哪几类(让你全面了解状态机模式的应用原理)(9)

三、状态机模式

上面通过迁移表的方式来实现状态机图,接下来就来介绍状态机模式,该设计模式也是比较经典的。它将状态逻辑与动作解耦,context是上下文对象,它主要实现动作功能,该状态机的模式的主要关键点是,状态对象持有context上下文对象的指针。

状态机有哪几类(让你全面了解状态机模式的应用原理)(10)

定义状态基类、状态A和状态B

状态机有哪几类(让你全面了解状态机模式的应用原理)(11)

接着实现定义状态基类、状态A和状态B

状态机有哪几类(让你全面了解状态机模式的应用原理)(12)

状态机有哪几类(让你全面了解状态机模式的应用原理)(13)

状态机有哪几类(让你全面了解状态机模式的应用原理)(14)

最后关键是定义上下文对象context, 其中声明State为Context的友元类,这表明在State类中可以访问 Context 类的 private 字段。

状态机有哪几类(让你全面了解状态机模式的应用原理)(15)

实现上下文对象context

状态机有哪几类(让你全面了解状态机模式的应用原理)(16)

实现完成所有状态机相关的代码之后,现在就来验证下状态的转移效果。

状态机有哪几类(让你全面了解状态机模式的应用原理)(17)

运行输出的打印信息如下,状态的转移从A到B,再到A。

状态机有哪几类(让你全面了解状态机模式的应用原理)(18)

四、boost startchart

boost startchart是boost实现的状态机库,它几乎支持了所有的UML中状态机的特性。

首先来看下一个简单的实现,初步了解其使用方法和机制。boost::statechart的状态机,它大量使用了CRTP, 基本思想要点是:派生类要作为基类的模版参数。更详细的原理可以参考《学会了这么神奇的模版模式,让你C 模版编程之路事半功倍》。首先需要实现继承state_machine的类Machine,其初始状态为Greeting。然后再实现继承simple_state的状态Greeting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(19)

然后看下如何启动和使用上面实现的”hello world”的功能。状态机Machine构建完成之后,需要调用initiate让它运行,并且进入初始状态Greeting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(20)

下面来实现稍微复杂的秒表功能,该秒表有两个按钮:开始/接收(Start/Stop) 和 重置(Reset), 对应两个状态Stopped和Running。其状态图如下所示。

状态机有哪几类(让你全面了解状态机模式的应用原理)(21)

首先定义两个事件EvStartStop和EvReset,所有事件都要继承event

状态机有哪几类(让你全面了解状态机模式的应用原理)(22)

然后实现继承state_machine的秒表StopWatch状态机,其初始状态为Active。

状态机有哪几类(让你全面了解状态机模式的应用原理)(23)

接着实现Active状态,m_dElapsedTime是记录当前秒表走的时长,simple_state接受四个参数,第一个参数当然就是Active本身,第二个参数因为Active是最外层的状态,所以要设置它所属的状态机为StopWatch,第三个参数则是设置Active的初始状态为Stopped。注意“typedef boost::statechart::transition< EvReset, Active > reactions; 的格式是固定的,表示如果收到EvReset事件,那么转移到Active状态。

状态机有哪几类(让你全面了解状态机模式的应用原理)(24)

定义IElapsedTime接口类,它由Running和Stopped两个状态来继承和实现

状态机有哪几类(让你全面了解状态机模式的应用原理)(25)

接着实现Stopped状态类,它指定Active为它的context, 这样它就会嵌套到Active中,这里实现的ElapsedTime函数,主要用于在Stopped状态下,StopWatch可以获取当前秒表的值。

状态机有哪几类(让你全面了解状态机模式的应用原理)(26)

然后再实现Running状态类,同样的,它也指定Active为它的context, 这样它就会嵌套到Active中。注意Running状态下使用context<Active>则直接访问Running的直接外层状态Active。

状态机有哪几类(让你全面了解状态机模式的应用原理)(27)

完成秒表的所有实现之后,现在就可以编写测试代码来测试状态的转移情况。

状态机有哪几类(让你全面了解状态机模式的应用原理)(28)

编译运行之后的打印信息如下,可以看出开始秒表的时长是0,发布EvStartStop事件之后,秒表的时长就不为0,当发布EvReset事件之后,秒表的时长再次变成0,说明重新进入了Active状态,m_dElapsedTime变量重置为0。

状态机有哪几类(让你全面了解状态机模式的应用原理)(29)

因为一个状态的context必须是一个完整的类型(即不可以是前向声明),所以状态机必须是由外而内进行定义,比如,上面秒表的总是从状态机(StopWatch)开始,接下来是外层的状态(Active), 最后才是外层状态的直接内层状态(Running/Stopped)。

秒表的功能已经介绍完成了,如果掌握了,就可以编写由几个状态组成的简单应用。对于稍多的状态,就需要“数码相机”登场了。一个状态可以由同一个事件触发的多个反应。这个就需要定制化反应。

假设一个数码相机有以下两个控制键,快门键和配置键。快门键分为快按和半按,对应事件为EvShutterHalf, EvShutterFull 和 EvShutterReleased;配置键对应事件为EvConfig。状态机图如下所示:

状态机有哪几类(让你全面了解状态机模式的应用原理)(30)

首先定义基本的事件,EvShutterHalf/EvShutterFull/EvShutterRelease/EvConfig/EvInFocus

状态机有哪几类(让你全面了解状态机模式的应用原理)(31)

实现数码相机的状态机Camera, 其初始状态为NotShooting

状态机有哪几类(让你全面了解状态机模式的应用原理)(32)

然后实现NotShooting状态,其初始内层状态为Idle, 注意我们这里使用了定制化反应custom_reaction, 这里只需指定事件,而实际的反应在react成员函数中实现。

状态机有哪几类(让你全面了解状态机模式的应用原理)(33)

实现Idle状态,其外层状态为NotShooting

状态机有哪几类(让你全面了解状态机模式的应用原理)(34)

实现Configuring状态,其外层状态为NotShooting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(35)

实现Shooting状态,其属于状态机Camera, Shooting的初始状态为Focusing。

状态机有哪几类(让你全面了解状态机模式的应用原理)(36)

实现Focusing状态,其外层状态为Shooting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(37)

实现Focused状态,其外层状态为Shooting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(38)

实现Storing状态,其外层状态为Shooting。

状态机有哪几类(让你全面了解状态机模式的应用原理)(39)

完成数码相机的所有实现之后,现在就可以开始进行验证效果。

状态机有哪几类(让你全面了解状态机模式的应用原理)(40)

运行输出的信息如下,发布不同的事件,就会执行不同的反应,并且切换到其他状态。

状态机有哪几类(让你全面了解状态机模式的应用原理)(41)

五、总结

至此,已经将基于迁移表实现的状态机,状态机设计模式以及boost statechart中的秒表和数码相机的功能介绍完毕。

迁移表实现的状态机关键点就是状态迁移关系正确存入映射表中,状态机设计模式则关键在于context上下文,其状态会持有该context对象指针,而boost statechart的状态机的实现,关键是要画出正确的状态图,然后依据状态图来定义事件、实现状态机、再实现外层状态,接着再实现内层状态。

这里介绍的状态机相关知识,只能说算是一个入门知识总结,更深入的议题还需要不断学习和实践来加深理解,比如异步状态机、历史、异常处理等。

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页