【嵌入式全套设计模式】吃透4大高频模式:简单工厂/适配器/注册器/策略模式(C语言实战+图解,零基础秒懂)
前言
在嵌入式裸机、RTOS、STM32实际项目开发中,很多开发者习惯直接堆砌大量 if-else / switch-case 完成业务逻辑。不管是外设驱动管理、多算法切换,还是兼容第三方老旧驱动,无脑硬编码只会导致代码耦合度爆炸、后期维护困难、新增功能牵一发而动全身。
嵌入式开发不需要掌握二十多种设计模式,80%的项目只用到4种核心模式:简单工厂模式、适配器模式、注册器模式、策略模式。
本文结合C语言+真实嵌入式硬件场景,搭配架构图解、完整可运行代码、适用场景,一站式吃透全套常用设计模式,帮你写出工业级低耦合、高拓展的插件化代码。
下面我从:核心定义、通俗图解、C语言实战、优缺点、适用场景五大维度,逐一讲解,并文末做全方位对比,彻底帮你区分易混淆的四大模式。
很多嵌入式小伙伴写代码时,都会写大量 if-else / switch。
比如:传感器滤波、屏幕刷新、通信校验、电机控制,不同模式写一堆判断。
代码又臭又长、耦合极高、新增功能必须修改主逻辑。
今天我给大家详细讲解 策略模式(Strategy Pattern),同时一次性帮大家梳理:简单工厂、适配器、注册器、策略模式 四大嵌入式最常用设计模式,彻底终结混淆。
一、简单工厂模式(对象创建专用)
1. 核心思想
一句话总结:统一创建对象,上层业务无需手动创建硬件实例。
简单工厂属于创建型设计模式,专门解决【频繁创建同类外设对象】的问题。由一个统一的工厂类/函数,集中管理所有设备的创建过程,上层业务只需要传入设备类型,即可直接获取对应对象,无需接触底层创建细节。
2. 架构图解
整体结构:抽象产品接口→多个具体硬件产品→统一工厂层→上层业务
上层业务层
│
▼
【简单工厂层】(switch统一分配创建对象)
┌──────────┬──────────┐
OLED驱动 LCD驱动 TFT驱动
3. 嵌入式实战场景
项目存在OLED、LCD两款屏幕,封装统一初始化、显示接口,由工厂函数根据类型自动创建对应屏幕对象。
4. 完整核心代码
#include <stdio.h>
#include <stdlib.h>
// 抽象产品:统一屏幕接口
typedef struct Screen{
void (*Init)(void);
void (*Show)(const char* str);
}Screen;
// 具体产品:OLED屏幕
void OLED_Init(void){ printf("OLED屏幕初始化完成\r\n"); }
void OLED_Show(const char* str){ printf("OLED显示:%s\r\n",str); }
// 具体产品:LCD屏幕
void LCD_Init(void){ printf("LCD屏幕初始化完成\r\n"); }
void LCD_Show(const char* str){ printf("LCD显示:%s\r\n",str); }
// 设备枚举类型
typedef enum{
SCREEN_OLED,
SCREEN_LCD
}ScreenType;
// 简单工厂:统一创建对象
Screen* Screen_Factory_Create(ScreenType type)
{
Screen* dev = (Screen*)malloc(sizeof(Screen));
if(dev == NULL) return NULL;
switch(type)
{
case SCREEN_OLED:
dev->Init = OLED_Init;
dev->Show = OLED_Show;
break;
case SCREEN_LCD:
dev->Init = LCD_Init;
dev->Show = LCD_Show;
break;
default: free(dev);return NULL;
}
return dev;
}
// 上层业务调用
int main(void)
{
Screen* scr = Screen_Factory_Create(SCREEN_OLED);
scr->Init();
scr->Show("简单工厂模式");
free(scr);
return 0;
}
5. 优缺点与适用场景
-
优点:分离对象创建与业务逻辑,屏蔽硬件底层,上层零感知硬件差异;
-
缺点:工厂内部依赖switch判断,新增外设必须修改工厂代码,违反开闭原则;
-
适用场景:外设固定、后期极少新增设备的中小型嵌入式项目。
1. 通俗一句话
同一个功能,多种不同算法,单独封装,动态切换,互不影响。
举个最简单的例子:
-
目的:去到目的地
-
策略:走路、骑车、开车、坐地铁
-
上层不用管怎么实现,随时切换出行方式
2. 解决什么痛点?
消灭超长的 if/else、switch,把不同的算法拆分成独立模块,实现算法与业务解耦。
二、适配器模式(接口兼容专用)
1. 核心思想
一句话总结:充当转接转接头,适配接口不兼容的第三方/老旧驱动。
适配器属于结构型设计模式,不负责创建对象、不管理设备,只做一件事:接口翻译与转换。当第三方驱动、老旧硬件的函数名、传参格式和项目自研标准接口不一致时,新增一层适配器做中转,无需修改原有驱动和上层业务。
2. 架构图解
上层业务(只调用标准接口)
│
▼
【适配器层(接口翻译)】
│
▼
老旧/第三方驱动(非标接口)
3. 嵌入式实战场景
项目原有标准屏幕接口为Init/Show,接入一款老旧第三方屏幕驱动,函数接口格式完全不匹配,通过适配器完成兼容适配。
4. 完整核心代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 系统标准接口
typedef struct Screen{
void (*Init)(void);
void (*Show)(const char* str);
}Screen;
// 第三方老旧非标驱动
void OldScreen_Open(void){ printf("老旧屏幕上电初始化\r\n"); }
void OldScreen_Display(const char* msg, int len){ printf("老旧屏幕显示:%s\r\n",msg); }
// 适配器:适配老旧驱动
void Adapter_Init(void){ OldScreen_Open(); }
void Adapter_Show(const char* str){ OldScreen_Display(str, strlen(str)); }
// 创建适配后的标准设备
Screen* Create_Screen_Adapter(void)
{
Screen* dev = (Screen*)malloc(sizeof(Screen));
dev->Init = Adapter_Init;
dev->Show = Adapter_Show;
return dev;
}
// 上层业务调用
int main(void)
{
Screen* scr = Create_Screen_Adapter();
scr->Init();
scr->Show("适配器模式");
free(scr);
return 0;
}
5. 优缺点与适用场景
-
优点:隔离非标驱动,上层业务零改动即可兼容第三方组件;
-
缺点:增加一层中间层,轻微增加代码量;
-
适用场景:对接老旧驱动、第三方库、开源外设驱动。
-
抽象策略:统一函数接口(C语言用函数指针结构体)
-
具体策略:每一种算法单独实现
-
上下文Context:统一调用入口,用来动态绑定/切换策略
三、注册器模式(设备管理专用,升级版工厂)
1. 核心思想
一句话总结:设备自主注册、全局表格管理、上层查表调用,彻底消灭switch。
注册器是嵌入式工程最常用的进阶模式,属于改良版创建型模式。摒弃简单工厂的switch硬编码,维护一张全局注册表;所有外设驱动初始化时主动注册到表格,上层通过设备名称/ID查表获取对象,完美符合开闭原则。
2. 架构图解
上层业务层(查表调用)
│
▼
【全局注册器注册表】
┌───────┬───────┬───────┐
OLED(已注册) LCD(已注册) 其他外设
3. 嵌入式实战场景
管理多款屏幕外设,所有屏幕自主注册到注册表,上层通过设备名称直接查找使用,新增外设无需修改注册器源码。
4. 完整核心代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEV_MAX_NUM 16
// 统一屏幕接口
typedef struct Screen{
const char* name;
void (*Init)(void);
void (*Show)(const char* str);
}Screen;
// 全局注册表
static Screen* g_ScreenReg[DEV_MAX_NUM];
static int g_DevCnt = 0;
// 注册设备
int Screen_Register(Screen* dev)
{
if(g_DevCnt >= DEV_MAX_NUM || dev == NULL) return -1;
g_ScreenReg[g_DevCnt++] = dev;
return 0;
}
// 按名称查找设备
Screen* Screen_Find(const char* name)
{
for(int i=0;i<g_DevCnt;i++)
{
if(strcmp(g_ScreenReg[i]->name,name) == 0)
return g_ScreenReg[i];
}
return NULL;
}
// 定义OLED设备并实现功能
void OLED_Init(void){ printf("OLED初始化\r\n"); }
void OLED_Show(const char* str){ printf("OLED:%s\r\n",str); }
Screen oled_dev = {"OLED",OLED_Init,OLED_Show};
// 定义LCD设备并实现功能
void LCD_Init(void){ printf("LCD初始化\r\n"); }
void LCD_Show(const char* str){ printf("LCD:%s\r\n",str); }
Screen lcd_dev = {"LCD",LCD_Init,LCD_Show};
// 统一注册入口
void Screen_Module_Init(void)
{
Screen_Register(&oled_dev);
Screen_Register(&lcd_dev);
}
// 上层业务
int main(void)
{
Screen_Module_Init();
Screen* scr = Screen_Find("OLED");
scr->Init();
scr->Show("注册器模式");
return 0;
}
5. 优缺点与适用场景
-
优点:彻底消灭switch,新增外设无需修改核心代码,插件化拓展;
-
缺点:需要维护全局注册表,占用少量内存;
-
适用场景:多外设管理、插件化架构、后期频繁新增硬件的中大型项目。
1. 业务场景
我们的屏幕拥有三种刷新模式:
-
正常全屏刷新:画质高、速度慢
-
局部区域刷新:只更新变动区域,节省性能
-
高速刷屏模式:舍弃画质,极致提速
我们需要在程序运行过程中 随时动态切换刷新策略。
2. 完整可运行代码
#include <stdio.h>
#include <stdlib.h>
// ===================== 1.抽象策略:统一刷新接口 =====================
typedef struct RefreshStrategy
{
// 刷新算法接口
void (*refresh)(void);
}RefreshStrategy;
// ===================== 2.具体策略:三种刷新算法 =====================
// 策略1:全屏正常刷新
void Normal_Refresh(void)
{
printf("【策略1】执行全屏正常刷新,画质优先\r\n");
}
// 策略2:局部区域刷新
void Part_Refresh(void)
{
printf("【策略2】执行局部区域刷新,性能均衡\r\n");
}
// 策略3:高速刷屏刷新
void Fast_Refresh(void)
{
printf("【策略3】执行高速刷屏模式,速度优先\r\n");
}
// ===================== 3.上下文:统一调用管理层 =====================
typedef struct
{
RefreshStrategy* strategy;
}Context;
// 设置当前策略(动态切换)
void Set_Context_Strategy(Context* ctx, RefreshStrategy* s)
{
if(ctx == NULL || s == NULL)
return;
ctx->strategy = s;
}
// 执行当前绑定的策略
void Context_Run(Context* ctx)
{
if(ctx != NULL && ctx->strategy->refresh != NULL)
{
ctx->strategy->refresh();
}
}
// ===================== 上层业务测试 =====================
int main(void)
{
// 定义三种策略对象
RefreshStrategy s1 = {Normal_Refresh};
RefreshStrategy s2 = {Part_Refresh};
RefreshStrategy s3 = {Fast_Refresh};
Context screenCtx;
// 切换为正常刷新
Set_Context_Strategy(&screenCtx, &s1);
Context_Run(&screenCtx);
// 切换为局部刷新
Set_Context_Strategy(&screenCtx, &s2);
Context_Run(&screenCtx);
// 切换为高速刷屏
Set_Context_Strategy(&screenCtx, &s3);
Context_Run(&screenCtx);
return 0;
}
3. 运行结果
【策略1】执行全屏正常刷新,画质优先
【策略2】执行局部区域刷新,性能均衡
【策略3】执行高速刷屏模式,速度优先
四、策略模式(算法切换专用)
1. 核心思想
一句话总结:同一功能、多种算法,独立封装,运行时动态切换。
策略模式属于行为型设计模式,和前面三种模式定位完全不同:它不管理设备、不创建对象、不适配接口,专门用于解决同一功能下多种算法逻辑切换的问题,替代业务层大量的if-else判断。
2. 架构图解
上层业务层
│
▼
【上下文Context(绑定策略)】
│
┌───────┬───────┬───────┐
全屏刷新 局部刷新 高速刷新(不同策略算法)
3. 嵌入式实战场景
屏幕刷新功能,封装全屏刷新、局部刷新、高速刷屏三种算法,程序运行中可自由切换刷新策略。
4. 完整可运行代码
策略模式在裸机、RTOS、Linux嵌入式项目中使用频率极高:
-
滤波算法:均值滤波、中值滤波、卡尔曼滤波动态切换
-
通信校验:和校验、CRC8、CRC16、CRC32 多种校验算法
-
屏幕显示:全屏刷新、局部刷新、动画刷新
-
电机控制:PID控制、BangBang控制、模糊控制
-
数据加密:不同加密/解码策略自由切换
四、策略模式优缺点
优点
-
彻底消灭臃肿的 if/switch 判断
-
算法互相独立,互不干扰,方便单独调试
-
运行时动态切换策略,灵活性拉满
-
新增算法无需修改上层代码,符合开闭原则
缺点
-
每一种策略都需要新增结构体与函数,项目文件会变多
-
对新手来说,前期理解成本比直接写if高
五、四大设计模式全方位对比(嵌入式必背)
1. 核心定位区别
|
设计模式 |
所属类型 |
核心职责 |
通俗口诀 |
解决问题 |
|---|---|---|---|---|
|
简单工厂 |
创建型 |
统一创建外设对象 |
我帮你造对象 |
分散的对象创建逻辑 |
|
适配器 |
结构型 |
兼容非标硬件接口 |
我帮你改接口 |
驱动接口不匹配 |
|
注册器 |
创建型(进阶) |
插件化管理所有外设 |
你来报名我管理 |
工厂switch臃肿、拓展性差 |
|
策略模式 |
行为型 |
动态切换业务算法 |
多种算法随便换 |
业务层大量if判断 |
2. 直白场景区分
-
想用统一方式创建OLED/LCD屏幕 → 简单工厂
-
第三方老旧屏幕接口和项目标准不一样 → 适配器模式
-
项目外设多、后期频繁新增,不想写switch → 注册器模式
-
滤波、CRC校验、屏幕刷新多种算法来回切换 → 策略模式
六、组合使用(工业级标准架构)
在成熟的嵌入式商业项目中,四种模式不会单独使用,最优组合方案:
适配器 + 注册器 + 策略模式
-
适配器:统一所有第三方、自研、老旧外设的接口;
-
注册器:集中管理所有适配完成的外设,实现插件化;
-
策略模式:针对单个外设的功能,切换不同算法逻辑;
该架构解耦程度拉满,也是大厂嵌入式项目的主流开发规范。
总结
嵌入式开发者无需盲目学习全部23种设计模式,吃透以上 4大核心模式,足以应对95%以上的裸机、RTOS、Linux嵌入式开发场景。
设计模式的本质从不是复杂化代码,而是解耦、复用、易拓展。根据业务场景择优使用,才能写出高质量、易维护的工业级C语言代码。
这是全网最直白的总结,帮你彻底区分四个容易混淆的模式:
1. 简单工厂模式
核心:只管【创建对象】
作用:统一创建硬件对象(OLED/LCD),上层不用手动new。弊端:内部依赖switch,新增硬件要改工厂。
2. 适配器模式
核心:只管【兼容接口】
作用:第三方驱动、老旧驱动接口不统一,写一层适配器做“翻译”,适配现有系统。
3. 注册器模式
核心:只管【统一管理】
作用:硬件自己注册、上层查表使用,干掉switch,升级版工厂模式,插件化开发。
4. 策略模式
核心:只管【切换算法】
作用:同一个功能,多种不同实现方式,动态切换算法(滤波、刷新、校验)。
六、一句话终极口诀
-
简单工厂:我帮你造对象
-
适配器模式:我帮你改接口
-
注册器模式:你来报名,我管理
-
策略模式:多种算法,随便换
总结
策略模式不做对象创建、不做接口适配、不做设备管理;它专门解决多算法切换的业务问题。
在嵌入式实际项目中,注册器 + 策略模式组合使用,就是工业级插件化架构。
更多推荐



所有评论(0)