嵌入式C语言编程:策略模式、状态模式和状态机的应用

概述

在没有面向对象语法的C语言中,策略(Strategy)模式和状态(State)模式都通过“上下文 + 接口”组合来模拟多态。
它们在代码结构上几乎一致,但设计意图和应用场景却差异很大。

本文分三部分深入剖析:

  1. 在C语言中如何模拟多态
  2. 策略模式与状态模式的设计意图与差异
  3. 结合EEPROM抽象层案例,展示二者协作与状态机应用

1. C语言中的“模拟多态”

在C语言中,多态可通过结构体与函数指针模拟:上下文(Context)持有一组函数指针(vtable),动态指向具体实现。下例展示通用框架:

// 通用接口类型(vtable)
typedef struct {
    void (*action)(void *ctx, int evt);
} module_iface_t;

// 通用上下文
typedef struct {
    const module_iface_t *iface;  // 指向当前策略或状态的 vtable
    void *instance_data;          // 指向具体策略/状态实例私有数据
} module_ctx_t;

在策略模式中,action 对应具体算法;在状态模式中,action 则为事件处理函数。此框架既可用于策略,也可用于状态,赋予系统极高的复用性。

2. 策略模式 vs. 状态模式

2.1 策略模式:关注“如何做”

  • 目的:封装一组可互换的算法,让调用者在运行时自行选择
  • 决策权:在外部,上下文被动执行指定策略
  • 应用场景:滤波算法切换、通信协议选择、后端存储后备机制等

示例类图

sensor_t
+filter_strategy_t *strat
+process()
«interface»
filter_strategy_t
+filter(buf, len)
mean_filter
kalman_filter

2.2 状态模式:关注“何时做、做什么”

  • 目的:将对象行为与内部状态绑定,使行为随状态自动切换
  • 决策权:在内部,状态自身决定是否及何时转换
  • 应用场景:设备驱动、协议状态机、时序复杂的后端服务等

示例类图与状态图

usb_dev_t
+usb_state_t *state
+handle_event(evt)
«interface»
usb_state_t
+handle(dev, evt)
st_disconnected
st_connecting
st_ready
st_error
EVT_PLUG_IN
EVT_ENUM_OK
EVT_ENUM_FAIL
EVT_PLUG_OUT
EVT_PLUG_OUT
Disconnected
Connecting
Ready
Error

核心差异

  • 策略模式:算法平行,可在任意时刻替换
  • 状态模式:状态有序,转换逻辑由状态机管理

2.3 小结:模式赋能架构

在前文中,我们分别阐述了策略模式如何将“如何做”的决策权交给外部,做到算法与调用逻辑的彻底解耦;以及状态模式如何把“何时做、做什么”的流程控制封装到内部状态机中,保障行为随状态演进而自动切换。

既然二者在实现上高度相似,我们有时候可以将二者组合起来使用。

3. 实战:高可靠EEPROM抽象层

下面通过分层设计,展示策略模式与状态机的协同应用。

3.1 统一层:策略模式应用

需求

为应用暴露统一接口,支持I2C EEPROM与NOR Flash两种后端,具备自动检测与运行时降级能力。

设计
  • 上下文unified_eeprom
  • 策略接口:包含 read_var, write_var 等函数指针
  • 具体策略I2C_EEPROM_ServiceEE_NOR_Flash_Service
  • 决策枚举eeprom_backend_t { AUTO, I2C, FLASH, HYBRID }
// 策略接口定义
typedef struct {
    rt_err_t (*read_var)(int id, void *buf, size_t len);
    rt_err_t (*write_var)(int id, const void *buf, size_t len);
} eeprom_iface_t;

// 统一上下文
typedef struct {
    const eeprom_iface_t *iface;
    eeprom_backend_t current_backend;
} unified_eeprom_t;
运行时降级
eeprom_set_backend(EEPROM_BACKEND_AUTO);

rt_err_t rc = eeprom_read_var(VAR_ID_X, buf, len);
if (rc != RT_EOK) {
    // 失败后自动切换策略
    eeprom_set_backend(EEPROM_BACKEND_FLASH);
    rc = eeprom_read_var(VAR_ID_X, buf, len);
}

AUTO模式下,统一层在I2C失败时自动切换至Flash。此决策过程对上层透明,上层仅感知最终结果。

3.2 后端服务:HSM驱动的状态模式

将I2C和Flash服务均设计为分层状态机,封装所有时序逻辑,实现可恢复与高可靠。

3.2.1 I2C EEPROM状态机
  • 关键状态UNINITIALIZEDIDLEREADINGWRITINGWAITING_READYVERIFYINGERROR
  • 核心逻辑:跨页读取、分页写入+轮询就绪、可选校验与指数退避重试
init_ok
EVT_READ_REQ
EVT_WRITE_REQ
page_done
ready
timeout
verify_ok
verify_fail
read_done
UNINITIALIZED
IDLE
READING
WRITING
WAITING_READY
VERIFYING
ERROR
3.2.2 NOR Flash状态机
  • 关键状态UNINITIALIZEDVALID_PAGEMIGRATINGFORMATTINGERROR
  • 核心逻辑:双页交替写、数据迁移、启动恢复与磨损均衡
init_ok
page_full
migration_done
write_fail
recoverable
format_done
UNINITIALIZED
VALID_PAGE
MIGRATING
ERROR
FORMATTING

4. 协同:策略 × 状态机

在“高可靠EEPROM抽象层”案例中,我们已经看到了策略模式与状态机的协同:

  • 策略层 (统一层):决定“走哪条路径”,即选择I2C后端还是Flash后端。
  • 状态机层 (后端服务):负责“路径内部的时序与恢复”,如I2C的写后轮询、Flash的页面迁移。

两者协同时,可在复杂约束下实现透明降级与高可用。

Appunified_eeprom(Strategy)I2C_Service(HSM)Flash_Service(HSM)eeprom_read_var(id, buf, len)策略=I2C优先ee_read_var(...)RT_ERROR触发降级策略fee_read_var(...)RT_EOKRT_EOKAppunified_eeprom(Strategy)I2C_Service(HSM)Flash_Service(HSM)

这种“策略包含状态机”的组合非常强大,下面我们深入探讨其两种主要协同模式。

4.1 模式一:将“完整状态机”作为策略单元

这是EEPROM案例的核心架构模式。我们把一个“完整的状态机(包含其所有状态、转移逻辑和动作)”封装成一个策略实现。Context仅需持有指向不同状态机策略的指针,即可在运行时“一键切换整体行为”。

这相当于将I2C_EEPROM_ServiceEE_NOR_Flash_Service这两个复杂的状态机,包装成符合eeprom_iface_t接口的策略单元。

unified_eeprom_t
+eeprom_iface_t *iface
+set_backend()
+read_var()
«interface»
eeprom_iface_t
+read_var()
+write_var()
I2C_EEPROM_Service
-hsm_t i2c_hsm
+read_var()
+write_var()
Flash_EEPROM_Service
-hsm_t flash_hsm
+read_var()
+write_var()
后端服务 (状态机实现)
统一层 (策略选择)
选择策略
降级切换
执行具体时序
执行具体时序
I2C HSM
Flash HSM
I2C Service Strategy
unified_eeprom
Flash Service Strategy

适用场景

  • 复杂流程的替换:将“I2C读写流程”与“Flash读写流程”抽象为可互换的策略。
  • 行为隔离:两套状态机逻辑完全独立,互不干扰,便于独立开发与测试。

4.2 模式二:在“状态内部”嵌入子策略

当状态机的主流程固定,但某个具体状态的“动作”或“算法”需要灵活替换时,可以在该状态内部嵌入一个“子策略”。

例如,在Flash_EEPROM_ServiceMIGRATING状态中,数据校验可以是一个子策略。我们可以根据系统要求,在“快速校验(CRC8)”和“高可靠校验(CRC32)”之间切换,而无需改变状态机的主体结构。

// MIGRATING状态的动作函数
void on_migrating_entry(hsm_t *sm, const hsm_event_t *event) {
    // ...
    // 根据运行时配置选择校验策略
    if (is_fast_mode()) {
        ctx->crc_strategy = &crc8_strategy;
    } else {
        ctx->crc_strategy = &crc32_strategy;
    }
    // ...
}

// 在迁移过程中使用子策略
void perform_migration_step(context_t *ctx) {
    // ...
    uint32_t crc = ctx->crc_strategy->calculate(data, len);
    // ...
}

适用场景

  • 算法替换:状态机结构稳定,但内部算法需要动态调整(如加密、压缩、校验算法)。
  • 功能开关:在不改变状态流程的情况下,通过空策略(noop)实现功能的动态开启或关闭。

4.3 C代码示例:用策略管理多状态机

下面的代码演示了“模式一”的通用实现,即如何通过策略模式来管理和切换两个完全独立的状态机流程。

// "状态机策略"的统一接口
typedef struct sm_ctx_s sm_ctx_t;
typedef struct {
    void (*init)(sm_ctx_t*);
    void (*dispatch)(sm_ctx_t*, int event);
} sm_strategy_t;

// 上下文,持有当前的状态机策略
struct sm_ctx_s {
    const sm_strategy_t *ops;
    void *state_data; // 指向具体状态机的私有数据
};

// --- 状态机A (例如: I2C EEPROM) ---
void sma_init(sm_ctx_t *c){ /* ... 状态机A初始化 ... */ }
void sma_dispatch(sm_ctx_t *c, int e){ /* ... A的事件处理 ... */ }
const sm_strategy_t smA_strategy = { sma_init, sma_dispatch };

// --- 状态机B (例如: Flash EEPROM) ---
void smb_init(sm_ctx_t *c){ /* ... 状态机B初始化 ... */ }
void smb_dispatch(sm_ctx_t *c, int e){ /* ... B的事件处理 ... */ }
const sm_strategy_t smB_strategy = { smb_init, smb_dispatch };

// 应用层通过策略接口与状态机交互
void application_logic(void) {
    sm_ctx_t fsm;

    // 根据条件选择一个完整的状态机流程作为当前策略
    if (use_i2c_backend()) {
        fsm.ops = &smA_strategy;
    } else {
        fsm.ops = &smB_strategy;
    }

    // 后续操作对具体状态机无感知
    fsm.ops->init(&fsm);
    while (has_event()) {
        fsm.ops->dispatch(&fsm, next_event());
    }
}

实践要点(结合统一EEPROM案例)

  • 策略层unified_eeprom负责选择 I2C 或 Flash 后端(含 AUTO/降级),对上层透明。
  • 状态机层i2c_eeprom_serviceee_nor_flash_service各自封装页写、迁移、恢复等复杂时序与错误处理。
  • 并发与锁序:策略层持短锁做选择,状态机在各自内部串行化操作,避免跨层嵌套锁。
  • 失败处理:策略层负责统计健康度并决策是否切换;状态机负责局部的、可恢复的重试。

5. 小结

  • 在嵌入式C中,可通过“上下文 + 接口”框架复用策略与状态模式实现结构。
  • 策略模式关注“如何做”,决策权在外部;状态模式关注“何时做、做什么”,决策权在内部。
  • 将策略与状态机结合,可构建透明、可降级、高可用的分层架构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值