嵌入式开发设计模式——状态机

状态机应该是最常用的设计模式了,而且在各种开发语言中,,甚至是硬件描述语言都常用。

状态机简单概念

状态机非常简单和直观,有两个基本概念:

  1. 状态(State):一个对象在某个时刻或某段时间,具有某种稳定的属性,就称其在某个状态。例如灯有“亮”、“灭”两种状态,门有“开”、“关”两种状态等。
  2. 迁移(Transition):从一种状态变到另一种状态就叫迁移。例如灯从亮到灭、从灭到亮,门从开到关、从关到开。

把状态和迁移画出来就成了状态图。从上面的例子也可以想到,有时迁移不会自动发生,例如门从关到开(也就是开门)是因为发生了“要出门”这个条件,并且迁移伴随着动作,也就是开门这个动作。

那么就可以画一个简单的状态图,用/前表示发生条件,/后表示动作

PS.这里的重点为了表现状态机的概念,实际问题中条件和动作可能更加复杂,例如需要区分准入条件和准出条件、进入动作和退出动作等。

状态机设计模式

当需要描述的对象或问题存在多种状态以及多种条件判断时,就可以考虑状态机模式了。画出状态图可以非常直观理解其变化。

状态机有横写和竖写两种方式,区别在于考虑的重点是在状态还是迁移上。有时对象是状态多但迁移条件简单,有时是状态少但迁移条件多。根据写出的代码长度就能看出来了,不需要特别区分。

上面门的例子用状态机来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typedef enum {
OPENED,
CLOSED
} state_t;

state_t door_state;

void door(void) {
switch (door_state) {
case CLOSED:
if (want_out()) {
open_door();
door_state = OPENED;
}
break;
case OPENED:
if (is_out()) {
close_door();
door_state = CLOSED;
}
break;
default:
// error handling
break;
}
}

另一种方式,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void want_out(void) {
if (door_state == CLOSED) {
open_door();
door_state = OPENED;
} else {
/* door is open, nothing to do */
}
}

void is_out(void) {
if (door_state == OPENED) {
close_door();
door_state = CLOSED;
} else {
/* door is closed, nothing to do */
}
}

像之前所说的,标准的状态机模式实现可能还包括很多其他条件和动作,但在实践中通常不会每个迁移每个状态都会用到所有的条件和动作,因此只需要记住状态和迁移的概念,在实现中只写用到的部分就可以了。