3. 用状态机替代复杂条件分支
任务目标
现有按键处理逻辑使用多层if-else嵌套判断按键事件与内部模式,导致分支复杂、难以扩展。本案例将其重构为有限状态机(FSM),使按键响应逻辑清晰、状态迁移可追踪。
重构前:复杂条件分支
// 重构前:模式+按键组合导致分支爆炸
void handle_key_legacy(int key, int *mode) {
if (*mode == 0) { // IDLE
if (key == 1) { *mode = 1; start_timer(); }
else if (key == 2) { *mode = 2; }
} else if (*mode == 1) { // RUN
if (key == 1) { *mode = 0; stop_timer(); }
else if (key == 2) { *mode = 3; }
else if (key == 3) { if (timer_running()) pause_timer(); }
} else if (*mode == 2) { // CFG
if (key == 1) { *mode = 0; }
else if (key == 2) { *mode = 1; }
else if (key == 3) { save_config(); }
} else if (*mode == 3) { // PAUSE
if (key == 1) { *mode = 0; stop_timer(); }
else if (key == 2) { *mode = 1; resume_timer(); }
}
}
问题:模式与按键组合呈N×M增长,新增模式或按键需修改多处嵌套,易漏改。
重构后:状态机实现
#include <stdint.h>
// 状态与事件定义
typedef enum { ST_IDLE, ST_RUN, ST_CFG, ST_PAUSE } state_t;
typedef enum { EV_KEY1, EV_KEY2, EV_KEY3, EV_NONE } event_t;
// 外部依赖(由硬件层提供)
void timer_start(void);
void timer_stop(void);
void timer_pause(void);
void timer_resume(void);
uint8_t timer_is_running(void);
void config_save(void);
// 状态迁移表: [当前状态][事件] = 目标状态
static const state_t trans_table[4][3] = {
/* ST_IDLE */ { ST_RUN, ST_CFG, ST_IDLE },
/* ST_RUN */ { ST_IDLE, ST_PAUSE, ST_RUN },
/* ST_CFG */ { ST_IDLE, ST_RUN, ST_CFG },
/* ST_PAUSE*/ { ST_IDLE, ST_RUN, ST_PAUSE },
};
// 动作表: [当前状态][事件] = 动作函数指针
typedef void (*action_t)(void);
static void act_none(void) {}
static void act_start(void) { timer_start(); }
static void act_stop(void) { timer_stop(); }
static void act_pause(void) { if (timer_is_running()) timer_pause(); }
static void act_resume(void){ timer_resume(); }
static void act_save(void) { config_save(); }
static const action_t action_table[4][3] = {
/* ST_IDLE */ { act_start, act_none, act_none },
/* ST_RUN */ { act_stop, act_pause, act_none },
/* ST_CFG */ { act_none, act_none, act_save },
/* ST_PAUSE*/ { act_stop, act_resume, act_none },
};
// 当前状态
static state_t current_state = ST_IDLE;
// 按键事件映射(硬件层调用)
event_t key_to_event(int key) {
switch (key) {
case 1: return EV_KEY1;
case 2: return EV_KEY2;
case 3: return EV_KEY3;
default: return EV_NONE;
}
}
// 状态机主处理函数
void handle_key_fsm(int key) {
event_t ev = key_to_event(key);
if (ev == EV_NONE) return;
uint8_t s = (uint8_t)current_state;
uint8_t e = (uint8_t)ev;
// 执行动作
action_table[s][e]();
// 状态迁移
current_state = trans_table[s][e];
}
关键设计说明
- 状态与事件解耦:新增状态只需扩展枚举和表行,新增按键只需扩展枚举和表列,无需修改处理函数主体。
- 动作与迁移分离:动作表处理副作用(定时器、存储),迁移表只负责状态跳转,逻辑互不干扰。
- 非法事件处理:表中非法组合映射到自身状态并执行
act_none,保证状态机始终有效。
运行与验证
- 编译(需提供上述外部函数的桩实现):
gcc -c fsm_key.c -o fsm_key.o - 最小验证代码(单独编译运行):
// test_fsm.c —— 桩实现与验证 #include <stdio.h> #include <assert.h> static int timer_running_flag = 0; void timer_start(void) { timer_running_flag = 1; } void timer_stop(void) { timer_running_flag = 0; } void timer_pause(void) { timer_running_flag = 0; } void timer_resume(void) { timer_running_flag = 1; } uint8_t timer_is_running(void) { return timer_running_flag; } void config_save(void) { printf("config saved\n"); } // 声明被测函数 void handle_key_fsm(int key); state_t get_state(void) { return current_state; } int main(void) { // 初始 IDLE,按 KEY1 → RUN,定时器启动 handle_key_fsm(1); assert(get_state() == ST_RUN); assert(timer_running_flag == 1); // RUN 按 KEY3 → 暂停(定时器停止) handle_key_fsm(3); assert(get_state() == ST_PAUSE); assert(timer_running_flag == 0); // PAUSE 按 KEY2 → 恢复 RUN handle_key_fsm(2); assert(get_state() == ST_RUN); assert(timer_running_flag == 1); // RUN 按 KEY1 → IDLE,定时器停止 handle_key_fsm(1); assert(get_state() == ST_IDLE); assert(timer_running_flag == 0); printf("All FSM tests passed.\n"); return 0; } - 编译并运行验证:
gcc -o test_fsm test_fsm.c fsm_key.c ./test_fsm - 通过条件:程序输出
All FSM tests passed.且无断言失败,说明状态迁移与动作执行均符合预期。
对比与收益
- 原
if-else版本新增一个模式需修改所有相关分支;状态机版本只需增加一行表项和对应动作函数。 - 状态迁移关系以表格形式集中呈现,代码审查时可直观检查所有合法路径。
- 非法按键组合被表驱动逻辑自动吸收,不会出现遗漏分支导致的未定义行为。