为什么嵌入式开发需要工厂模式?
工厂模式就好比一家电子产品代工厂。下单生产智能手表时,你不需要自己造一条流水线,只要告诉工厂“我要圆形表盘的型号”,对方自然会把对应产品交给你。嵌入式开发中的工厂模式,正是这样一条“智能生产线”。
此前已经讨论过 最简单很好用的设计模式及裸机上的应用-单例,今天我们继续用 C 语言在单片机上实现设计模式中的 工厂模式。
在 STM32 开发中经常遇到这样的场景:系统需要支持多种同类型设备,比如不同型号的传感器、通讯模块。传统做法里往往充斥着大量的 switch-case 判断:
void Sensor_Read(void * data)
{
switch(sensor_type) {
case BMP180:
BMP180_Read(data);
break;
case DHT11:
DHT11_Read(data);
break;
// 新增类型需要修改这里
}
}
这样一来,Sensor_Read 会随着设备类型增加而越写越长。有没有更好的方式?工厂模式正好可以解决这个问题。
C语言实现工厂模式(基于STM32)
既然是工厂模式,自然要先有一个工厂类,由它负责产出产品对象。工厂需要掌握产品的主要特征——就像服装工厂,每一件衣服都有自己独立的样式、颜色、尺寸和材料。
工厂模式的几个关键部分包括:产品接口、具体产品、工厂类和注册机制。在 C 语言里,结构体中的 函数指针 可以模拟接口。每个具体产品(比如不同的传感器)都要实现这些函数,工厂类则负责创建具体实例,同时提供一个注册函数用来添加新的产品类型。
定义设备接口
typedef struct {
void (*Init)(void);
float (*Read)(void);
void (*Calibrate)(float offset);
} SensorInterface;
如果这些产品还有一些公共特征,也可以额外增加私有变量。
实现具体设备
BMP180 气压传感器驱动:
static void BMP180_Init()
{ /* 初始化代码 */ }
static float BMP180_Read()
{ /* 读取数据 */ }
static void BMP180_Calibrate(float offset)
{ /* 校准 */ }
const SensorInterface BMP180_Sensor = {
.Init = BMP180_Init,
.Read = BMP180_Read,
.Calibrate = BMP180_Calibrate
};
DHT11 温湿度传感器驱动:
static void DHT11_Init()
{ /* 初始化代码 */ }
static float DHT11_Read()
{ /* 读取数据 */ }
static void DHT11_Calibrate(float offset)
{ /* 校准 */ }
const SensorInterface DHT11_Sensor = {
.Init = DHT11_Init,
.Read = DHT11_Read,
.Calibrate = DHT11_Calibrate
};
创建工厂类
typedef struct {
SensorInterface* sensors[MAX_SENSORS];
uint8_t count;
} SensorFactory;
void SensorFactory_Register(SensorFactory* factory, SensorInterface* sensor)
{
if(factory->count < MAX_SENSORS) {
factory->sensors[factory->count++] = sensor;
}
}
SensorInterface* SensorFactory_Create(SensorFactory* factory, uint8_t index)
{
return (index < factory->count) ? factory->sensors[index] : NULL;
}
使用示例
int main(void)
{
SensorFactory factory = {0};
// 注册可用传感器
SensorFactory_Register(&factory, &BMP180_Sensor);
SensorFactory_Register(&factory, &DHT11_Sensor);
// 动态选择传感器BMP180_Sensor
SensorInterface* currentSensor = SensorFactory_Create(&factory, 0);
if(currentSensor) {
currentSensor->Init();
float data = currentSensor->Read();
// 处理数据...
}
// 动态选择传感器DHT11_Sensor
SensorInterface* currentSensor = SensorFactory_Create(&factory, 1);
if(currentSensor) {
currentSensor->Init();
float data = currentSensor->Read();
// 处理数据...
}
}
顺带提一句:示例里两次 SensorInterface* currentSensor = ... 都放在同一个 main 函数作用域内,主要是为了演示注册与获取的流程,实际工程中建议用不同变量名或拆到子函数中,避免重复定义。
鉴于这是在单片机上运行,示例没有采用动态内存分配,而是提前用数组约定容量。每次先注册产品类型,需要用的时候再根据注册的位置去工厂取。由于工厂类产出的产品对象都遵循统一接口,因此拿到接口指针后,就能直接访问不同对象的初始化和读取方法。
工厂模式带来的优势
最后总结一下,工厂模式主要带来四个方面的提升:
- 模块解耦:对象创建与使用分离
- 扩展方便:新增设备类型无需修改核心逻辑
- 维护简单:设备管理集中化,耦合度降低
- 代码复用:统一接口规范,减少重复代码
云栈社区后续也会持续整理这类嵌入式工程化实践笔记。工厂模式就聊到这里,快用到项目里去吧。