State状态模式推导过程以及完整实现
近日周立功教授公开了数年的心血之作《程序设计与数据结构》,电子版已无偿性分享到电子工程师与高校群体下载,经周立功教授授权,特对本书内容进行连载。
>>> 1.1.1 动作类
前面详细介绍了State状态模式的推导过程以及完整的实现,采用了简单的打印语句作为作为4个动作的实现示例。然而,实际动作是很有可能发生变化的,由于动作直接在事件处理方法中执行。比如,LOCKED状态的card事件处理方法定义为:
1 void locked_card(turnstile_t *p_turnstile)
2 {
3 turnstile_state_set(p_turnstile, &unlocked_state);
4 printf("unclock\n"); // 执行unlock动作
5 }
由此可见,只要动作发生变化,都必须修改事件处理方法。基于此,不妨将闸机动作单独封装在一个动作类中,详见图 4.12。
图 4.12 状态机类图
如程序清单4.23和程序清单4.24所示为动作类的声明和实现,为何要为这么简单的动作创建类呢?因为只有预测变换和管理变化才能拥抱变化,只有这样才能使软件具有可扩展性和可维护性。
程序清单4.23动作函数声明(turnstile_action.h文件内容)
1 #pragma once
2
3 void turnstile_action_lock(void);
4 void turnstile_action_unlock(void);
5 void turnstile_action_alarm(void);
6 void turnstile_action_thankyou(void);
程序清单4.24动作函数实现(turnstile_action.c文件内容)
1 void turnstile_action_lock(void)
2 {
3 printf("clock\n");
4 }
5
6 void turnstile_action_unlock(void)
7 {
8 printf("unclock\n");
9 }
10
11 void turnstile_action_alarm(void)
12 {
13 printf("alarm\n");
14 }
15
16 void turnstile_action_thankyou(void)
17 {
18 printf("thank you\n");
19 }
程序中的alarm、unlock、thankyou和lock动作对应的动作函数分别为: alarm()、unlock()、thankyou()和lock()。当将4个动作分别由4个函数实现时,则具体动作从状态机中分离出来了。比如,LOCKED状态下的card事件处理方法定义为:
1 void locked_card(turnstile_t *p_turnstile)
2 {
3 turnstile_state_set(p_turnstile, &unlocked_state);
4 turnstile_action_unlock (); // 执行unlock动作
5 }
这是一种良好的设计,因为动作接口优雅地解除了FSM的状态变换逻辑和它要执行的动作之间的耦合。这样一来就算另外一个具有完全不同逻辑的FSM,也可以在没有任何影响的情况下使用这些动作接口。
由于在处理动作时,不需要任何数据,它是一个只有方法,没有属性的动作类,因此没有刻意使用结构体为其定义专门的类型。而实际的动作类可能会包含一些数据,其定义如下:
typedef struct _turnstile_action {
// some data
} turnstile_action_t;
此时,当动作发生变化时,仅需修改动作类的函数
- 电源软启动的实用设计技巧(07-16)
- 周立功:动态分布内存——malloc()函数与calloc()函数(07-22)
- 周立功“程序设计与数据结构”:深度解剖动态分布内存的free()函数与realloc()函数(07-25)
- 周立功教你学程序设计技术:做好软件模块的分层设计,回调函数要这样写(07-30)
- 周立功教你学C语言编程:教你数组是如何保存指针的(07-31)
- 算法的泛化问题,这些坑你可能都经历过!|周立功教你学软件设计(08-01)