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

1. 概述

在没有面向对象语法的C语言中,策略(Strategy)模式和状态(State)模式都通过"上下文 + 接口"组合来模拟多态。

它们在代码结构上几乎一致,但设计意图和应用场景却差异很大。

本文将结合典型的滤波算法和USB设备示例,深入解析两者的实现差异与选型建议。

2. 两种模式介绍

  • 策略模式:将一组算法封装起来,调用者在运行时决定使用哪一种,关注"如何做"。

策略模式类图
sensor_t +filter_strategy_t *strat +data[128] <<interface>> filter_strategy_t +filter(buf, len) mean_strat kalman_strat

  • 状态模式:让对象在内部状态变化时自行切换行为,关注"何时做、做什么"。

状态模式类图
usb_dev_t +usb_state_t *state <<interface>> usb_state_t +handle(dev, evt) st_disconnected st_connecting st_ready st_error

状态转换图
EVT_PLUG_IN EVT_ENUM_OK EVT_ENUM_FAIL Disconnected Connecting Ready Error

3. 实现结构其实类似

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

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

上述结构在策略和状态模式下均可重用:仅需替换 action 策略函数或 handle_event 状态函数。

4. 策略模式详解

4.1 典型场景

一个传感器需要根据功耗或精准度要求,动态选用不同滤波算法。

4.2 代码示例

c 复制代码
// 策略接口
typedef struct {
    void (*filter)(int *buf, int len);
} filter_strategy_t;

// 上下文
typedef struct {
    const filter_strategy_t *strat;
    int data[128];
} sensor_t;

// 均值滤波
void mean_filter(int *buf, int len) { /* ... */ }
const filter_strategy_t mean_strat = { .filter = mean_filter };

// 卡尔曼滤波
void kalman_filter(int *buf, int len) { /* ... */ }
const filter_strategy_t kalman_strat = { .filter = kalman_filter };

// 应用层切换策略
void sensor_process(sensor_t *s) {
    if (is_low_power()) {
        s->strat = &mean_strat;
    } else {
        s->strat = &kalman_strat;
    }
    s->strat->filter(s->data, 128);
}

4.3 策略模式特点

  • 切换由外部决定
  • 上下文仅负责调用,不关心何时切换
  • 适合算法族、可插拔的行为

5. 状态模式详解

5.1 典型场景

一个USB设备在不同生命周期阶段:断开、连接中、就绪、错误,各阶段有不同处理逻辑,并能自动流转。

5.2 代码示例

c 复制代码
typedef struct usb_dev_s usb_dev_t;

// 状态接口
typedef struct {
    void (*handle)(usb_dev_t *dev, int evt);
} usb_state_t;

// 上下文
struct usb_dev_s {
    const usb_state_t *state;
    // 设备相关数据
};

// 断开状态
void st_disconnected(usb_dev_t *d, int evt) {
    if (evt == EVT_PLUG_IN) {
        d->state = &st_connecting;
        start_enumeration();
    }
}
const usb_state_t st_disconnected = { .handle = st_disconnected };

// 连接中状态
void st_connecting(usb_dev_t *d, int evt) {
    if (evt == EVT_ENUM_OK) {
        d->state = &st_ready;
    } else if (evt == EVT_ENUM_FAIL) {
        d->state = &st_error;
    }
}
const usb_state_t st_connecting = { .handle = st_connecting };

// 事件分发
void usb_event(usb_dev_t *d, int evt) {
    d->state->handle(d, evt);
}

6. 策略模式与状态模式的状态机化融合

6.1 概述

实现了策略模式与状态模式的深度融合,既能在运行时无缝切换不同流程,也能在状态内部灵活替换子算法,为复杂嵌入式控制系统提供高内聚、低耦合、可配置的可复用框架。

在嵌入式系统(如固件升级、多协议网关、自适应控制)中,通常面临两类变化点:

  • 横向变化:整条业务流程需要整体替换(例如升级流程与正常流程)。
  • 纵向变化:流程内部某一状态的子算法需要动态替换(例如根据网络质量切换可靠传输与快速传输)。

如果采用传统的switch-case 实现,常会引发以下困境:

  • 新增流程时需要阅读理解整份代码。
  • 运行时切换流程通常需重启或清零全局变量,造成业务中断。
  • 难以针对单一流程进行单元测试证。

6.2 策略-状态两级抽象

实现了策略模式与状态模式的深度融合,既能在运行时无缝切换不同流程,也能在状态内部灵活替换子算法,为复杂嵌入式控制系统提供高内聚、低耦合、可配置的可复用框架。

第一级:整状态机作为策略

将完整状态机(含状态表、变量、定时器与转移逻辑)封装为统一策略接口 sm_strategy_t

Context 在运行时只需持有对该接口的指针,即可灵活切换整个状态机实现。

c 复制代码
sm_ctx_t fsm;
fsm.ops = use_upgrade ? &sm_upgrade : &sm_normal;
fsm.ops->init(&fsm);

第二级:状态内部再嵌策略

在某个具体状态的处理函数中,根据运行时条件动态注入子策略,以替换子算法或业务动作。

c 复制代码
/* 在 Transferring 状态中,根据网络质量选择子策略 */
ctx->sub = is_lossy() 
    ? &reliable_xfer_strategy 
    : &fast_xfer_strategy;
ctx->sub->chunk_size = calc_chunk_size(ctx->rtt);
ctx->sub->send(ctx->sub, evt);

两级策略各自独立:

  • 第一级解决"流程级"整体切换。
  • 第二级解决"状态级"内部微调。

6.3 静态结构

sm_ctx_t +const sm_strategy_t *ops +void *state_data <<interface>> sm_strategy_t +init(sm_ctx_t*) +dispatch(sm_ctx_t*, int) sm_normal sm_upgrade

6.4 运行时视图

选择策略 normal upgrade Application strategy sm_normal FSM sm_upgrade FSM state1 state2 stateA stateB

6.5 关键实现细节

策略接口定义

c 复制代码
typedef struct sm_ctx_s sm_ctx_t;
typedef struct {
    void (*init)(sm_ctx_t*);
    void (*dispatch)(sm_ctx_t*, int);
} sm_strategy_t;

运行时上下文

c 复制代码
struct sm_ctx_s {
    const sm_strategy_t *ops;   /* 当前流程策略 */
    void                *state_data;
};

零停机切换

通过原子写入指针即可完成业务流程切换,无需中断或重启。

c 复制代码
void switch_strategy(sm_ctx_t *ctx, const sm_strategy_t *new_ops)
{
    new_ops->init(ctx);
    ctx->ops = new_ops;    /* 原子赋值,零停机 */
}

状态内子策略示例

c 复制代码
typedef struct {
    void (*send)(const void *self, int event);
    size_t chunk_size;
} xfer_strategy_t;

static void st_transferring(sm_ctx_t *ctx, int evt)
{
    xfer_strategy_t *sub = select_xfer_strategy(ctx->rtt);
    sub->chunk_size = calc_chunk(ctx->rtt);
    sub->send(sub, evt);
}
相关推荐
二级小助手2 小时前
C语言二级考试环境配置教程【window篇】
c语言·全国计算机二级·c语言二级·二级c语言·全国计算机二级c语言·c二级
kyle~2 小时前
C/C++---前缀和(Prefix Sum)
c语言·c++·算法
lidashent3 小时前
c语言-内存管理
java·c语言·rpc
这里没有酒3 小时前
[C语言] 指针的种类
c语言
_OP_CHEN3 小时前
数据结构(C语言篇):(二)顺序表
c语言·数据结构·学习笔记·入门·顺序表·动态顺序表·静态顺序表
knd_max3 小时前
C语言:数据在内存中的存储
c语言
YxVoyager4 小时前
【C++标准库】<ios>详解基于流的 I/O
c语言·c++
j_xxx404_8 小时前
数据结构:单链表的应用(力扣算法题)第一章
c语言·数据结构·算法·leetcode
long3169 小时前
状态设计模式
java·学习·程序人生·设计模式·状态模式·state-pattern
源远流长jerry9 小时前
STM32之Systick与基础定时器
c语言·stm32·单片机·嵌入式硬件