星标公众号,让嵌入式知识 “投喂” 不停歇!
一、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口用于创建相关或依赖对象的家族,而无需指定它们的具体类。
在需要高兼容性的嵌入式系统中,抽象工厂模式能显著降低多平台适配成本,确保硬件组件间的兼容性,是构建可移植嵌入式框架的核心模式。
上一篇我们分享了:嵌入式编程模型 | 简单工厂模式
抽象工厂模式(Abstract Factory Pattern)与简单工厂模式有很多相似之处。我们先做个对比:
| 特性 | 简单工厂模式 | 抽象工厂模式 |
|---|---|---|
| 嵌入式应用 |
|
|
| 适用场景 |
|
|
| 抽象级别 |
|
|
| 创建对象 |
|
|
| 工厂类型 |
|
|
| 扩展性 |
|
|
简单工厂模式适用于产品类型少且变化不频繁的场景。如嵌入式中对单一设备驱动管理,比如管理LCD驱动:
抽象工厂模式适用于需要创建多个相关产品的系统。如嵌入式中对整个硬件平台的管理。
抽象工厂模式的核心包含四个关键部分:
-
抽象工厂(Abstract Factory):声明创建一系列产品的方法。 -
具体工厂(Concrete Factory):实现抽象工厂的方法,创建具体产品。 -
抽象产品(Abstract Product):声明产品接口。 -
具体产品(Concrete Product):实现抽象产品接口,定义具体产品。
其中,第2~4点就是简单工厂模式的要点。即简单工厂模式加上第1点的抽象工厂这个要点就构成了抽象工厂模式。
二、嵌入式:多平台硬件抽象层
设备需要支持不同平台(STM32/ESP32等),每个平台有兼容的输入、输出驱动。
1、代码
C语言版本:
#include <stdio.h>
#include <stdbool.h>
// 抽象产品(Abstract Product)
typedefstruct {
void (*Init)(void);
void (*Draw)(int x, int y);
} DisplayDriver;
typedefstruct {
void (*Init)(void);
bool (*ReadButton)(void);
} InputDriver;
// 具体产品实现 - STM32平台
void stm32_disp_init(void) {
printf("STM32 Display Initializedn");
}
void stm32_draw(int x, int y) {
printf("STM32 Drawing at (%d, %d)n", x, y);
}
void stm32_button_init(void) {
printf("STM32 Button Initializedn");
}
bool stm32_read_button(void) {
printf("STM32 Button Readn");
returntrue; // 模拟按下状态
}
// 具体产品实现 - ESP32平台
void esp32_disp_init(void) {
printf("ESP32 Display Initializedn");
}
void esp32_draw(int x, int y) {
printf("ESP32 Drawing at (%d, %d)n", x, y);
}
void esp32_button_init(void) {
printf("ESP32 Button Initializedn");
}
bool esp32_read_button(void) {
printf("ESP32 Button Readn");
returnfalse; // 模拟未按下状态
}
// 抽象工厂(Abstract Factory)
typedefstruct {
DisplayDriver display;
InputDriver input;
} HWPlatform;
// 具体工厂(Concrete Factory) - STM32平台
const HWPlatform stm32_platform = {
{stm32_disp_init, stm32_draw},
{stm32_button_init, stm32_read_button}
};
// 具体工厂(Concrete Factory) - ESP32平台
const HWPlatform esp32_platform = {
{esp32_disp_init, esp32_draw},
{esp32_button_init, esp32_read_button}
};
int main() {
// 选择平台 - 通过定义USE_STM32宏来选择
#if defined(USE_STM32)
constchar* platform_name = "STM32";
const HWPlatform* platform = &stm32_platform;
#else
constchar* platform_name = "ESP32";
const HWPlatform* platform = &esp32_platform;
#endif
printf("Running on %s platformn", platform_name);
// 初始化显示
platform->display.Init();
// 初始化输入
platform->input.Init();
// 绘制图形
platform->display.Draw(10, 20);
platform->display.Draw(30, 40);
// 读取按钮状态
bool buttonState = platform->input.ReadButton();
printf("Button state: %sn", buttonState ? "PRESSED" : "RELEASED");
return0;
}
C ++版本:
#include <iostream>
#include <cstdint>
// 抽象产品接口
struct DisplayDriver {
void (*Init)(void);
void (*Draw)(int x, int y);
};
struct InputDriver {
void (*Init)(void);
bool (*ReadButton)(void);
};
// 具体产品实现 - STM32平台
namespace STM32 {
void DisplayInit() {
std::cout << "[STM32] Display initializedn";
}

void DisplayDraw(int x, int y) {
std::cout << "[STM32] Drawing at (" << x << ", " << y << ")n";
}
void InputInit() {
std::cout << "[STM32] Input initializedn";
}
bool InputReadButton() {
std::cout << "[STM32] Reading buttonn";
returntrue; // 模拟按钮被按下
}
}
// 具体产品实现 - ESP32平台
namespace ESP32 {
void DisplayInit() {
std::cout << "[ESP32] Display initializedn";
}
void DisplayDraw(int x, int y) {
std::cout << "[ESP32] Drawing at (" << x << ", " << y << ")n";
}
void InputInit() {
std::cout << "[ESP32] Input initializedn";
}
bool InputReadButton() {
std::cout << "[ESP32] Reading buttonn";
returnfalse; // 模拟按钮未被按下
}
}
// 抽象工厂
class HWPlatform {
public:
const DisplayDriver& GetDisplayDriver() const { return display; }
const InputDriver& GetInputDriver() const { return input; }
virtual void PrintPlatformInfo() const {
std::cout << "Generic Hardware Platformn";
}
protected:
DisplayDriver display;
InputDriver input;
};
// 具体工厂 - STM32平台
class STM32Platform :public HWPlatform {
public:
STM32Platform() {
display.Init = STM32::DisplayInit;
display.Draw = STM32::DisplayDraw;
input.Init = STM32::InputInit;
input.ReadButton = STM32::InputReadButton;
}
void PrintPlatformInfo() const override {
std::cout << "n=== STM32 Hardware Platform ===n";
}
};
// 具体工厂 - ESP32平台
class ESP32Platform :public HWPlatform {
public:
ESP32Platform() {
display.Init = ESP32::DisplayInit;
display.Draw = ESP32::DisplayDraw;
input.Init = ESP32::InputInit;
input.ReadButton = ESP32::InputReadButton;
}
void PrintPlatformInfo() const override {
std::cout << "n=== ESP32 Hardware Platform ===n";
}
};
// 使用示例
int main() {
std::cout << "=== Embedded Hardware Platform Demo ===n";
// 平台选择
#if defined(USE_STM32)
STM32Platform platform;
std::cout << "Selected platform: STM32n";
#else
ESP32Platform platform;
std::cout << "Selected platform: ESP32n";
#endif
// 打印平台信息
platform.PrintPlatformInfo();
// 初始化硬件
std::cout << "nInitializing hardware...n";
platform.GetDisplayDriver().Init();
platform.GetInputDriver().Init();
// 使用显示驱动
std::cout << "nDrawing on display...n";
platform.GetDisplayDriver().Draw(5, 10);
platform.GetDisplayDriver().Draw(15, 25);
platform.GetDisplayDriver().Draw(30, 40);
// 读取输入状态
std::cout << "nReading input...n";
bool buttonState = platform.GetInputDriver().ReadButton();
std::cout << "Button state: " << (buttonState ? "PRESSED" : "RELEASED") << "n";
return0;
}
2、优缺点分析
(1)优点
① 符合开闭原则
开闭原则 (The Open/Closed Principle, OCP) :软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。
-
初始支持:STM32和ESP32 -
新增平台:添加Nordic nRF52支持 -
新增 NRF52Platform工厂类 -
新增nRF52显示/输入驱动 -
无需修改现有STM32/ESP32代码
② 接口统一化
// 统一的硬件操作接口
platform->display.Init();
platform->input.Init();
platform->display.Draw(10, 20);
platform->display.Draw(30, 40);
bool buttonState = platform->input.ReadButton();
③ 平台无关性设计
-
业务逻辑与硬件解耦 -
代码复用率高,减少平台移植工作量(新平台只需实现具体工厂和产品)
(2)缺点
① 扩展新产品困难
例如:
-
初始设计:显示+输入 -
新增需求:摄像头模块 -
修改成本: -
修改所有3个平台工厂类 -
添加3个摄像头驱动实现 -
更新所有测试用例
三、总结:何时选择抽象工厂
-
当需要管理单个产品类型(如LCD驱动)时 → 简单工厂 -
当需要管理相关产品族(如整套硬件驱动)时 → 抽象工厂