当前位置: 首页 > news >正文

嵌入式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. 实现结构其实类似

// 通用接口类型(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 代码示例

// 策略接口
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 代码示例

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 在运行时只需持有对该接口的指针,即可灵活切换整个状态机实现。

sm_ctx_t fsm;
fsm.ops = use_upgrade ? &sm_upgrade : &sm_normal;
fsm.ops->init(&fsm);

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

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

/* 在 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 关键实现细节

策略接口定义

typedef struct sm_ctx_s sm_ctx_t;
typedef struct {void (*init)(sm_ctx_t*);void (*dispatch)(sm_ctx_t*, int);
} sm_strategy_t;

运行时上下文

struct sm_ctx_s {const sm_strategy_t *ops;   /* 当前流程策略 */void                *state_data;
};

零停机切换

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

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

状态内子策略示例

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);
}
http://www.dtcms.com/a/318776.html

相关文章:

  • 东莞立晟精密硅胶科技有限公司将携重磅产品亮相 AUTO TECH China 2025 广州国际汽车技术展
  • 计算机网络1-4:计算机网络的定义和分类
  • 汽车娱乐信息系统域控制器的网络安全开发方案
  • FPGA实战:用PL端串口发送Hello world
  • 【C/C++】C++引用和指针的对比
  • 29-数据仓库与Apache Hive-创建库、创建表
  • 树莓派安装OpenCV环境
  • 【CDA案例】数据分析案例拆解:解锁数据分析全流程!
  • 微服务、服务网格、Nacos架构与原理
  • mapbox进阶,mapbox-gl-draw绘图插件扩展,绘制新增、编辑模式支持点、线、面的捕捉
  • Linux系统编程--权限管理
  • 在NVIDIA Orin上用TensorRT对YOLO12进行多路加速并行推理时内存泄漏(下)
  • Redis为什么要引入多线程?
  • 如何在GPU上安装使用Docker
  • 【AI】——SpringAI通过Ollama本地部署的Deepseek模型实现一个对话机器人(二)
  • 用 tcpdump 捕获网络数据包
  • RTSP播放器技术详解:功能支持、平台覆盖与快速集成指南
  • PostgreSQL 强制索引:当重复数据让优化器“失明”时的解决方案
  • centos系统sglang单节点本地部署大模型
  • Sklearn 机器学习 数据降维PCA 自己实现PCA降维算法
  • 如何打造一支AI时代下的IT团队,为企业战略目标快速赋能
  • Java面试宝典:JVM的垃圾收集算法
  • MCU中的晶振(Crystal Oscillator)
  • 【Zephyr】02_从零教你开发芯片级ADC驱动(HAL层篇)
  • 每日五个pyecharts可视化图表-bars(6)
  • 嵌入式硬件中MOSFET基本原理与实现
  • 基于 Socket.IO 实现 WebRTC 音视频通话与实时聊天系统(Spring Boot 后端实现)
  • C语言中级_动态内存分配、指针和常量、各种指针类型、指针和数组、函数指针
  • MATLAB科研数据可视化
  • cuda编程笔记(13)--使用CUB库实现基本功能