状态机、状态模式

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

状态机、状态模式
什么是状态机?
有限状态机,英⽂翻译是 Finite State Machine,缩写为 FSM,简称为状态机。

状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。

其中,事件也称为转移条件(Transition Condition)。

事件触发状态的转移及动作的执⾏。

不过,动作不是必须的,也可能只转移状态,不执⾏任何动作。

实现状态机的⽅法有多种,⽐较常⽤的有分⽀逻辑法、查表法、状态模式。

我们以⼀个简单的 CD 播放器为例⼦。

这个例⼦⾥⾯只有状态、事件,不包含动作
简单CD播放器的按键与按键的功能
按键功能
[Play/Pause]播放/暂停
[Stop]停⽌
状态迁移图:
状态机实现⽅式⼀:分⽀逻辑法
它的核⼼思想是根据状态迁移图,要么先确定状态、要么先确定事件,直译代码。

⽅法分析:对于简单状态机,该法是可以接受的。

但是,对于复杂的状态机,这种实现极易漏写或错写某个状态转移;代码中充斥⼤量if-else或switch-case 分⽀判断逻辑,可读性和可维护性差。

如下就是先确定事件,然后再在事件内根据状态进⾏状态转移。

1 typedef enum {
2 ST_IDLE,
3 ST_PLAY,
4 ST_PAUSE
5 } State;
6
7 typedef enum {
8 EV_PLAY_PAUSE,
9 EV_STOP
10 } Event;
11
12 State state;
13
14// 初始化
15void initialize() {
16 state = ST_IDLE;
17 }
18
19// play or pause
20void playOrPause() {
21if (state == ST_IDLE) {
22 state = ST_PLAY;
23 } else if (state == ST_PLAY) {
24 state = ST_PAUSE;
25 } else if (state == ST_PAUSE) {
26 state = ST_PLAY;
27 }
28 }
29
30// stop
31void stop() {
32if (state == ST_PLAY || state == ST_PAUSE) {
33 state = ST_IDLE;
34 }
35 }
36
37// 事件响应
38void onEvent(Event ev) {
39switch (ev) {
40case EV_PLAY_PAUSE:
41 playOrPause();
42break;
43case EV_STOP:
44 stop();
45break;
46default:
47break;
48 }
49 }
状态机实现⽅法⼆:查表法
状态机除了⽤状态转移图表⽰外,还可以⽤⼆维表表⽰。

如下第⼀维表⽰当前状态,第⼆维表⽰事件,值表⽰当前状态经过事件之后,转移到的新状态即执⾏的动作。

CD播放器状态迁移表
状态/事件停⽌(EV_STOP)播放/暂停(EV_PLAY_PAUSE)
空闲(ST_IDLE)忽略开始播放并转为播放状态
播放(ST_PLAY)停⽌并转为空闲状态停⽌并转为暂停状态
暂停(ST_PAUSE)停⽌并转为空闲状态播放并转为播放状态
相⽐于分⽀逻辑的实现⽅式,查表法的代码实现更加清晰,可读性和可维护性更好。

当修改状态机时,只需要修改transitionTable和actionTable两个⼆维数组即可。

1 typedef enum {
2 ST_IDLE,
3 ST_PLAY,
4 ST_PAUSE
5 } State;
6
7 typedef enum {
8 EV_STOP,
9 EV_PLAY_PAUSE
10 } Event;
11
12 State state;
13
14// transitionTable[i][j]表⽰状态为i时,发⽣事件j后,状态迁移到transitionTable[i][j]
15 State transitionTable[][2] = {
16 {ST_IDLE, ST_PLAY},
17 {ST_IDLE, ST_PAUSE},
18 {ST_IDLE, ST_PLAY}
19 };
20
21// 初始化
22void initialize() {
23 state = ST_IDLE;
24 }
25
26 State executeEvent(Event ev) {
27return transitionTable[state][ev];
28 }
29
30// play or pause
31void playOrPause() {
32 state = transitionTable[state][EV_PLAY_PAUSE];
33 }
34
35// stop
36void stop() {
37 state = transitionTable[state][EV_STOP];
38 }
39
40// 事件响应
41void onEvent(Event ev) {
42switch (ev) {
43case EV_PLAY_PAUSE:
44 playOrPause();
45break;
46case EV_STOP:
47 stop();
48break;
49default:
50break;
51 }
52 }
53
54int main(void) {
55 initialize();
56 onEvent(EV_PLAY_PAUSE); // 播放
57 printf("%d ", state);
58 onEvent(EV_PLAY_PAUSE); // 暂停
59 printf("%d ", state);
60 onEvent(EV_STOP); // 停⽌
61 printf("%d\n", state);
62 }
执⾏结果为:1 2 0
状态机实现⽅式三:状态模式
事件触发的动作如果⾮常简单,可以⽤⼆维的actionTable表⽰,则可以使⽤查表法。

但是,如果执⾏的动作是⼀系列复杂的逻辑操作(⽐如加减积分、写数据库、发送消息等),就没有办法⽤简单的⼆维数组来表⽰了。

参考资料
1.
2.《C现代编程集成开发环境、设计模式、极限编程、测试驱动开发、重构、持续集成》。

相关文档
最新文档