一、抽象工厂模式
抽象工厂模式(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个摄像头驱动实现更新所有测试用例
702