一、抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是一種創建型設計模式,它提供一個接口用于創建相關或依賴對象的家族,而無需指定它們的具體類。
在需要高兼容性的嵌入式系統中,抽象工廠模式能顯著降低多平臺適配成本,確保硬件組件間的兼容性,是構建可移植嵌入式框架的核心模式。
抽象工廠模式(Abstract Factory Pattern)與簡單工廠模式有很多相似之處。我們先做個對比:
特性簡單工廠模式抽象工廠模式嵌入式應用單一設備驅動管理整套硬件平臺適配適用場景產品類型少且變化不頻繁需要創建多個相關產品的系統抽象級別產品級別抽象工廠級別抽象創建對象單個產品產品家族(多個相關產品)工廠類型單一工廠類抽象工廠+多個具體工廠實現類擴展性添加新產品需修改工廠類添加新產品族只需新增工廠類
簡單工廠模式適用于產品類型少且變化不頻繁的場景。如嵌入式中對單一設備驅動管理,比如管理LCD驅動:
抽象工廠模式適用于需要創建多個相關產品的系統。如嵌入式中對整個硬件平臺的管理。
抽象工廠模式的核心包含四個關鍵部分:
- 抽象工廠(Abstract Factory):聲明創建一系列產品的方法。
- 具體工廠(Concrete Factory):實現抽象工廠的方法,創建具體產品。
- 抽象產品(Abstract Product):聲明產品接口。
- 具體產品(Concrete Product):實現抽象產品接口,定義具體產品。
其中,第2~4點就是簡單工廠模式的要點。即簡單工廠模式加上第1點的抽象工廠這個要點就構成了抽象工廠模式。
二、嵌入式:多平臺硬件抽象層
設備需要支持不同平臺(STM32/ESP32等),每個平臺有兼容的輸入、輸出驅動。
1、代碼
#include
#include
// 抽象產品(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 Initialized\n");
}
void stm32_draw(int x, int y) {
printf("STM32 Drawing at (%d, %d)\n", x, y);
}
void stm32_button_init(void) {
printf("STM32 Button Initialized\n");
}
bool stm32_read_button(void) {
printf("STM32 Button Read\n");
returntrue; // 模擬按下狀態
}
// 具體產品實現 - ESP32平臺
void esp32_disp_init(void) {
printf("ESP32 Display Initialized\n");
}
void esp32_draw(int x, int y) {
printf("ESP32 Drawing at (%d, %d)\n", x, y);
}
void esp32_button_init(void) {
printf("ESP32 Button Initialized\n");
}
bool esp32_read_button(void) {
printf("ESP32 Button Read\n");
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 platform\n", 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: %s\n", buttonState ? "PRESSED" : "RELEASED");
return0;
}
C ++版本:
#include
#include
// 抽象產品接口
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 initialized\n";
}
void DisplayDraw(int x, int y) {
std::cout << "[STM32] Drawing at (" << x << ", " << y << ")\n";
}
void InputInit() {
std::cout << "[STM32] Input initialized\n";
}
bool InputReadButton() {
std::cout << "[STM32] Reading button\n";
returntrue; // 模擬按鈕被按下
}
}
// 具體產品實現 - ESP32平臺
namespace ESP32 {
void DisplayInit() {
std::cout << "[ESP32] Display initialized\n";
}
void DisplayDraw(int x, int y) {
std::cout << "[ESP32] Drawing at (" << x << ", " << y << ")\n";
}
void InputInit() {
std::cout << "[ESP32] Input initialized\n";
}
bool InputReadButton() {
std::cout << "[ESP32] Reading button\n";
returnfalse; // 模擬按鈕未被按下
}
}
// 抽象工廠
class HWPlatform {
public:
const DisplayDriver& GetDisplayDriver() const { return display; }
const InputDriver& GetInputDriver() const { return input; }
virtual void PrintPlatformInfo() const {
std::cout << "Generic Hardware Platform\n";
}
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: STM32\n";
#else
ESP32Platform platform;
std::cout << "Selected platform: ESP32\n";
#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();