状态机——新的开端

前言

经过一个多月的反复思考和艰难抉择,最新版状态机终于完成!此版状态机吸取 SYZKLibrary 第一版、第二版的丰富实践经验,兼顾状态机原理,并融合 Stateless 之精华,用词简练,功能齐全,极具观赏和实用价值。

使用方法

1. 定义状态

每个状态机具有一系列状态,这些状态不一定都要比状态机更早定义,但构造状态机时必须至少有一个已知的有效状态,即状态机初始状态。

状态的定义包含4个可选项:

  • 1)动作(函数),状态机在当前状态所要执行的具体动作;

  • 2)前判断条件,满足状态的前判断条件才可进入状态,可用于约束初始状态,也可用于初始化资源;

  • 3)后判断条件,满足状态的后判断条件才可退出状态,也可用于释放资源;

  • 4)是否允许自动自循环,当状态动作执行完毕且没有合适的目标状态时,状态再次执行动作还是跳转到null。

  • 同一个状态机必须具有相同类型的状态,且必须实现提供这些可选项的 IState 接口。

我们推荐以下两种方法定义状态:

使用枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum class StateTest : IState {
Init {
override val loop = false
override fun doing() = run { i = 0 }
},
Add {
override val loop = false
override fun doing() = run { ++i; Unit }
override fun before() = i < 20
},
Print {
override val loop = false
override fun doing() = run { println(i) }
}
}

使用 DSL

1
2
3
4
5
6
7
8
9
10
11
12
val init = state {
doing { i = 0 }
}

val add = state {
doing { i++ }
before { i < 20 }
}

val print = state {
doing { println(i) }
}

2. 定义状态机

1
val `for` = StateMachine(Init)

提供初始状态即可。

3. 构造或注册事件

所谓事件,就是使状态机发生状态转移的函数。状态机提供了方法来构造事件。

1
val event = `for`.event(Print to Init)

通过状态机的引用可以构造事件,只需传递事件的源状态和目的状态即可。事件发生(调用)时,状态机会检查当前状态是否与状态的源匹配,以及两端节点是否接受状态转移。有时,用户可能并不急切地要求状态转移,而仅仅希望事件在状态的动作之间发生,这时可以将事件注册到状态机,状态机将在合适的时机自动调用。

使用 register 方法:

1
`for` register (Init to Print)

基于定义,此模型也支持无限状态状态机,甚至在运行间动态添加新的状态和事件。

示例

完整示例代码可以在 这里 找到。