自动状态机

自动状态机是一项非常实用的编程技巧,可以解决一些复杂的逻辑问题,通常来说如果一个系统有多种状态,不同状态之间的切换逻辑不尽相同,那么就可以考虑使用状态机。比如提取字符串的token,网络协议的处理,复杂界面等。

对于状态机的基本定义,在此就不赘述了,可以自行翻阅Wiki.

实现一个自动状态机不是一件很难的事,主要的工作还是在于将实际问题转映射成状态机模型。至于具体的实现方法,有几种固定的模式可循:

使用switch语句

最直接也是最简单的方法,代码写起来就是这个样子:

switch state:
    case state1:
        state = newState(inputData)
        doAction(state1, state)
        break;

    case state2:
        ...
        break;

    default:
        ...
        break;

Table Driven的方式

switch语句的实现方式不太容易维护,如果要扩展状态机就需要修改比较多的地方,容易导致Bug。如果把一些切换逻辑存在一个table(二维数组)里面,那么代码看起来就会简洁很多,而且扩展相对容易。从目前遇到的实际情况来说,有两个地方可以使用table:

  • 记录状态切换的表, 简称为state_table。如下面的例表中,X轴为输入(比如字符‘A’-‘Z’),Y轴为状态,表中的一项(x,y)代表状态y在输入x的情况下,会跳转到哪个状态。(-1表示不切换):

state

  • 记录状态切换之后的action的表,简称为atction_table。表中的一项(x,y)表示从状态y切换到状态x需要调用的函数,表里存的是函数指针:

action

于是代码便可以写成这样:

newState = state_table[state][input]
action_table[state][newState]
state = newState

值得一提的是,很多时候输入并不能简单的量化,如在编写界面的时候,用户的各种操作以及其它模块的消息通知等等难以用一个维度来表示,这个时候也可以考虑在state_table存状态切换函数的指针。

state

那么代码可以写成:

newState = state_table[state](input)
action_table[state][newState]
state = newState

State Design Pattern

使用面对对象的方法来实现State Machine。简单来说:

State为状态类,定义了进入这个状态和离开这个状态时会调用的函数,就是上面所说的action。Event类表示状态的切换逻辑,这个类中包含Source State和Target State。State是状态图中的圆圈,Event是其中的连接线。

State Machine包含所有State和所有的Event,State Machine类设置完毕之后,外部调用只需要调用这个类的接口就可以模仿State Macine的运行了。

Github上的TransitionKit是一个很好的oc例子,偷个懒不写Sample了,可以参考它上面的。

这个方法的好处是把状态的逻辑抽象出来了,可以在其他地方复用,而且想要扩张状态机相对容易,再添加State和相应的Event就可以了,而不要修改其他部分的代码。从实现方式来看这种方法的效率比前面的应该会差一点,不过这对系统的整体性能应该不会有太多影响。个人还是比较喜欢用Table Driven的方式,看起来比较直观简洁,总体代码量比较少。