嵌入式驱动工厂模式:接口+实现+工厂三件套

发布时间:2026/10/3 20:38:50

嵌入式驱动工厂模式:接口+实现+工厂三件套 上一篇聊数组和链表选型有朋友私信问业务代码里到处spi_flash_read()、spi_flash_write()换个存储介质就要改一大片这种耦合怎么破这问题太典型了答案是工厂模式把创建驱动和使用驱动分开。工厂模式在应用软件开发里是老面孔但嵌入式里很多人没用过觉得我就一个驱动搞什么工厂。真到产品线一铺开SPI Flash、SDIO Flash、EEPROM 三种存储要兼容就傻眼了。这篇把嵌入式驱动工厂模式讲透给个能直接套的模板。先看痛点业务代码直接依赖具体驱动我刚工作时写过一个数据记录模块长这样void log_save(uint32_t addr, const uint8_t *data, uint32_t len) { spi_flash_write(addr, data, len); /* 直接调 SPI Flash 驱动 */ } void log_read(uint32_t addr, uint8_t *buf, uint32_t len) { spi_flash_read(addr, buf, len); }当时觉得没问题。后来产品要出高低配两个版本高配用 SPI Flash低配用 EEPROM。我一改就傻了log_save/log_read里全是spi_flash_xxx每个调用点都要改成e2prom_xxx要么加if (high_version)分支代码瞬间丑陋。更要命的是还有个 SDIO Flash 的中配版本在路上。这就是业务代码直接依赖具体驱动的代价换硬件就改业务代码加一种硬件就加一坨分支。业务逻辑什么时候存、存哪里和驱动细节怎么写 Flash搅在一起改一个动全身。解法抽象一个驱动接口工厂模式的第一步不是写工厂是抽象接口。把存储设备能干什么抽象出来业务代码只依赖接口不依赖具体驱动。/* 存储设备接口抽象出所有存储设备共有的操作 */ typedef struct { int (*init)(void); int (*read)(uint32_t addr, uint8_t *buf, uint32_t len); int (*write)(uint32_t addr, const uint8_t *data, uint32_t len); int (*erase)(uint32_t addr, uint32_t len); const char *name; } StorageDevice;这个结构体就是 C 语言里接口的标准写法一组函数指针。它定义了存储设备这个抽象类型该有什么行为但不关心具体是 SPI Flash 还是 EEPROM。业务代码改成依赖这个抽象接口static const StorageDevice *g_storage NULL; void log_save(uint32_t addr, const uint8_t *data, uint32_t len) { g_storage-write(addr, data, len); /* 调接口不调具体驱动 */ } void log_read(uint32_t addr, uint8_t *buf, uint32_t len) { g_storage-read(addr, buf, len); }注意g_storage-write这一行业务代码不再认识spi_flash或e2prom它只知道有个存储设备能 write。具体是哪种设备运行时再定。这就是依赖抽象不依赖具体。具体驱动实现接口每种具体驱动实现这个接口把自己填进函数指针表/* SPI Flash 驱动实现 */ static int spi_flash_init(void) { /* 初始化 SPI Flash */ return 0; } static int spi_flash_read(uint32_t addr, uint8_t *buf, uint32_t len) { /* ... */ return 0; } static int spi_flash_write(uint32_t addr, const uint8_t *data, uint32_t len) { /* ... */ return 0; } static int spi_flash_erase(uint32_t addr, uint32_t len) { /* ... */ return 0; } const StorageDevice spi_flash_dev { .init spi_flash_init, .read spi_flash_read, .write spi_flash_write, .erase spi_flash_erase, .name spi_flash, }; /* EEPROM 驱动实现 */ static int e2prom_init(void) { /* 初始化 EEPROM */ return 0; } static int e2prom_read(uint32_t addr, uint8_t *buf, uint32_t len) { /* ... */ return 0; } static int e2prom_write(uint32_t addr, const uint8_t *data, uint32_t len) { /* ... */ return 0; } static int e2prom_erase(uint32_t addr, uint32_t len) { /* ... */ return 0; } const StorageDevice e2prom_dev { .init e2prom_init, .read e2prom_read, .write e2prom_write, .erase e2prom_erase, .name e2prom, };每个驱动就是一个StorageDevice实例把自己的函数填进去。这其实就是 C 语言的面向对象结构体当类函数指针当方法实例当对象。上一篇讲链表管 GUI 时用过类似手法这里是同一个思路在不同场景的应用。工厂函数根据参数返回不同实例接口和实现都有了谁来决定用哪个这就是工厂函数的活根据参数返回对应的驱动实例typedef enum { STORAGE_SPI_FLASH, STORAGE_SDIO_FLASH, STORAGE_E2PROM, } StorageType; const StorageDevice *storage_factory(StorageType type) { switch (type) { case STORAGE_SPI_FLASH: return spi_flash_dev; case STORAGE_SDIO_FLASH: return sdio_flash_dev; case STORAGE_E2PROM: return e2prom_dev; default: return NULL; } }业务代码初始化时调一次工厂拿到驱动实例之后就用这个实例int log_init(StorageType type) { g_storage storage_factory(type); if (g_storage NULL) return -1; return g_storage-init(); }现在回到开头的痛点高配 SPI Flash、低配 EEPROM、中配 SDIO Flash。用工厂模式后业务代码一行不用改只在初始化时传不同的StorageType。log_init(STORAGE_SPI_FLASH)跑高配log_init(STORAGE_E2PROM)跑低配。换硬件、加硬件全在工厂函数一处搞定。三个优势一个比一个值钱工厂模式在嵌入式驱动里的优势我列三条一条比一条值钱。第一业务代码不依赖具体驱动。这是基本盘。业务逻辑和驱动细节解耦换硬件不动业务代码。这一条就值回票价产品线一铺开维护成本断崖式下降。第二新增驱动类型只改两处。加一种新存储比如 NAND Flash只要写一个nand_flash_dev实例实现接口再在工厂函数加一个case。业务代码、其他驱动一行不用动。这符合开闭原则对扩展开放对修改封闭。在多人协作的项目里这个特性让新增驱动不会误伤老代码。第三便于单元测试 Mock 替换。这条很多人没意识到。业务代码依赖的是StorageDevice接口测试时可以塞一个 Mock 实现/* 测试用 Mock 驱动 */ static int mock_read(uint32_t addr, uint8_t *buf, uint32_t len) { memcpy(buf, mock_mem[addr], len); /* 从内存读不碰真硬件 */ return 0; } const StorageDevice mock_dev { .read mock_read, /* ... */ }; void test_log_save(void) { g_storage mock_dev; /* 替换成 Mock */ log_save(0x100, test_data, 4); assert(memcmp(mock_mem[0x100], test_data, 4) 0); }不用接真硬件就能测业务逻辑。嵌入式单元测试一直是个老大难硬件依赖重、跑得慢工厂模式加 Mock 是破局的关键之一。我见过不少团队嵌入式代码零测试理由是没法跑硬件其实业务逻辑层完全可以用 Mock 测起来只是架构上没留接口。落地建议第一接口别设计太大。只放真正共有的操作别为了统一硬塞。比如 EEPROM 没有擦除概念按字节写接口里硬加erase就得让 EEPROM 实现个空函数污染抽象。这种差异要么接口分两层要么用可选函数指针NULL 表示不支持调用前判断。第二工厂函数用 switch 没问题别被switch 是坏味道洗脑。嵌入式里驱动类型有限、编译期基本确定switch 直白高效。真要动态注册插件式再用链表注册表那是过度设计。第三实例用静态全局变量const StorageDevice spi_flash_dev不要每次工厂调用都 malloc。驱动实例是编译期就确定的单例静态分配即可省去内存管理也避免碎片。第四工厂可以分层。storage_factory选存储类型sensor_factory选传感器类型display_factory选显示类型。每类硬件一个工厂别一个工厂管所有。这套模式不挑 MCU不挑 RTOS核心就是接口 实现 工厂三件套。它真正解决的是硬件多样性和业务逻辑稳定性之间的矛盾业务逻辑要稳硬件要灵活换工厂模式用一层抽象把它们隔开。在产品线多、硬件版本多的项目里这是必修课。下一篇讲应用层感知底层变化的三种姿势轮询、回调、观察者把驱动和应用之间的另一条线也理清。有用的话点个在看让更多被驱动耦合折磨的嵌入式工程师看到。标签嵌入式 工厂模式 驱动设计 C语言面向对象
延伸阅读

更多相关文章

2026/10/3 20:35:49

蓝牙芯片驱动开发-第5章第11题-在蓝牙数据传输中如何利用DMA实现流控

蓝牙面试题解析:在蓝牙数据传输中,如何利用 DMA 实现流控? 难度:⭐⭐⭐⭐ 较难 | 场景:社招二面/三面、蓝牙驱动优化 | 高频:🔥🔥🔥🔥 标准答案 DMA 与流控结合通过 硬件流控(CTS/RTS)+ 软件缓冲状态 + DMA 暂停/恢复 实现蓝牙数据流的平滑传输: ① DMA 在蓝…

2026/10/3 20:35:49

ZeroTermux 内置命令手册深度解析:bzip2 压缩与解压实战指南

移动开发开发工具 【免费下载链接】ZeroTermux 项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTermux 点击查看 免费下载 本指南以 ZeroTermux 项目内置的命令参考文档 bzip2.md 为核心,系统讲解 .bz2 格式压缩包创建与管理的完整用法&#xf…

2026/10/2 8:16:46

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/2 18:20:53

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/3 15:02:19

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/3 0:04:31

国内大学生必备的AI写作辅助软件是哪款?

国内高校学生在论文写作过程中,越来越依赖AI辅助工具提升效率,主流方案以本土化全流程工具为核心,结合通用大模型与专业插件,覆盖选题构思、框架搭建、初稿撰写、查重降重、格式调整等关键环节,本文将深入解析当前主流…

2026/10/3 0:04:31

Codex接入Jev模型完整指南:配置方法、本地部署与踩坑排查

最近不少人在讨论 Codex 搭配 Jev 这套玩法,我一开始没太当回事,直到自己把 Jev 接进 Codex跑了几轮编码任务之后,才明白那些说“直接起飞”的人是怎么想的。Codex 作为工具本身已经够能打了,但模型固定、上下文策略固定&#xff…

2026/10/3 0:04:31

GitHub 热门: NVIDIA/Model-Optimizer

👋 Hi,我擅长 AI 大模型应用落地、意识解码与 AI 开发工具链 。 💡 创业路上,用技术换时间,一起把 AI 变成生产力 🚀 >GitHub 热门: NVIDIA/Model-Optimizer 凌晨两点,你刚把跑通了的 Qwen3.…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑