一、状态机描述
状态机理论最初的发展在数字电路设计领域。在数字电路方面,根据输出是否与输入信号有关,状态机可以划分为Mealy型和Moore型状态机;根据输出是否与输入信号同步,状态机可以划分为异步和同步状态机。而在软件设计领域,状态机设计的理论俨然已经自成一体。Moore型状态机的输出只和当前状态有关,和输入无关,如果在软件设计领域设计出这种类型的状态机,则该状态机接受的事件都是无内蕴信息的事件(输入)。Mealy型状态机的输入是由当前状态和输入共同决定,对应到软件设计领域,则该状态机接收的事件含有内蕴信息,并且影响状态机的输出。显然,这种划分在软件设计领域毫无意义。虽然软件设计领域的状态机也有同步和异步的划分,但和数字电路方面的同步异步已经不同。
除了《数字电路》,涉及到状态机的课程就是《编译原理》了(本人属计算机专业,其它专业是否涉及到状态机就不清楚了)。下面简单回顾一下《编译原理》里有关有限状态机的描述。在编译原理课程里面,对有限状态机的描述仅限在编译领域,特定状态,针对输入字符,发生状态改变,没有额外的行为,另编译原理里有限状态机的构成要素,还包含唯一的初始状态和一个终态集。数学语言描述如下:一个有限状态机M是一个五元组,M=(K,E,T,S,Z)。其中(1)K是一个有穷集,其中的每个元素称为状态(2)E是一个有穷字母表,它的每个元素称为一个输入字符(3)T是转换函数,是K×E->K上的映射(4)S是K中的元素,是唯一的一个初态(5)
Z是K的一个子集,是一个终态集,或者叫结束集。很明显,状态机在编译原理里的讲解已经特化,输入被定位为字符集,状态改变的时候没有额外动作发生。
与编译原理中的状态机不同,软件设计领域中通用状态机的输入不是字符集,而是被称作事件的结构(可以是结构体,也可以是类对象),并且特定的状态下,针对发生的事件,不仅发生状态改变,而且产生动作。借鉴编译原理中状态机的初始状态和终态,通用状态机的数学语言描述如下:一个通用有限状态机M是一个七元组,M={K,E,T,M,F,S,Z}。其中(1)K是一个有穷集,其中的每个元素称为状态(2)E是一个有穷集,它的每个元素称为一个事件(3)T是转换函数,是K×E->K上的映射(4)M是一个有穷集,它的每个元素称为动作(5)F是动作映射函数,是K×E->M上的映射(6)S是K中的元素,是唯一的一个初态(7)
Z是K的一个子集,是一个终态集,或者叫结束集。实用的状态机可以做进一步的优化,首先,可以把 (3)(5)整合在一起,做一个K×E->{K,M}的映射,其次从实用性的角度出发,禁止状态接收空事件(无输入的情况下,状态发生改变),作为弥补,为每个状态增加进入动作和离开动作,第三,鉴于定时器在系统中,尤其是在状态机中的重要性,可以为每个状态增加定时器以及超时后的状态转换。本文后面的讲述以及实现暂不考虑把定时器特化,如果需要,可以在状态的进入动作中初始化定时器(另:关于定时器,以后会写文章《系统设计之
定时器》)。
二、状态机分类(后文中如无特别说明,则状态机指软件设计领域的通用有限状态机)
依据状态之间是否有包含关系,分以下两种
(1)常规状态机。状态机中的所有状态是不相交的、互斥的。
(2)层次状态机。状态机中的状态之间要么是互斥的,要么是真包含的,可以用树性结构来描述这些状态集,包含其它状态的状态称为枝节点,不包含其它状态的状态称为叶节点,为方便单树描述,总是设计一个状态包含所有的状态节点,称为根节点。状态机的状态只能停留在叶节点,而不能停留在枝节点,每个枝节点需要指定一个子节点为它的默认子节点,以便状态机进入枝节点的时候能够停留到叶节点。
三、状态机实现
(1)switch/case if/else方式实现。用于少量状态(3个及其以下)的时候,不需要引入专门的状态机模块。这种方式不能编写通用的状态机模块,不再多说。
(2)面向过程方式:宏是实现面向过程方式的通用方式。虽然在状态机层面还是可以用面向对象的方式封装,这里还是把它称为面向过程的方式。
1.常规状态机模块实现。这个状态机涉及到机构由上而下为:
- 顶层结构是状态机:当前状态id,缺省操作,状态表,
- 状态表:状态数组
- 状态结构:状态id,状态名,进入操作,退出操作,缺省操作,状态事件表(数组)
- 状态事件结构:操作,事件,下一状态的id
状态机的算法是由状态机的结构决定的。实现如下:
|