一、抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是一種創(chuàng)建型設計模式,它提供一個接口用于創(chuàng)建相關或依賴對象的家族,而無需指定它們的具體類。
在需要高兼容性的嵌入式系統(tǒng)中,抽象工廠模式能顯著降低多平臺適配成本,確保硬件組件間的兼容性,是構建可移植嵌入式框架的核心模式。
上一篇我們分享了:嵌入式編程模型 | 簡單工廠模式
抽象工廠模式(Abstract Factory Pattern)與簡單工廠模式有很多相似之處。我們先做個對比:
特性 | 簡單工廠模式 | 抽象工廠模式 |
---|---|---|
嵌入式應用 | 單一設備驅動管理 | 整套硬件平臺適配 |
適用場景 | 產品類型少且變化不頻繁 | 需要創(chuàng)建多個相關產品的系統(tǒng) |
抽象級別 | 產品級別抽象 | 工廠級別抽象 |
創(chuàng)建對象 | 單個產品 | 產品家族(多個相關產品) |
工廠類型 | 單一工廠類 | 抽象工廠+多個具體工廠實現(xiàn)類 |
擴展性 | 添加新產品需修改工廠類 | 添加新產品族只需新增工廠類 |
簡單工廠模式適用于產品類型少且變化不頻繁的場景。如嵌入式中對單一設備驅動管理,比如管理LCD驅動:
抽象工廠模式適用于需要創(chuàng)建多個相關產品的系統(tǒng)。如嵌入式中對整個硬件平臺的管理。
抽象工廠模式的核心包含四個關鍵部分:
- 抽象工廠(Abstract Factory):聲明創(chuàng)建一系列產品的方法。具體工廠(Concrete Factory):實現(xiàn)抽象工廠的方法,創(chuàng)建具體產品。抽象產品(Abstract Product):聲明產品接口。具體產品(Concrete Product):實現(xiàn)抽象產品接口,定義具體產品。
其中,第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;
// 具體產品實現(xiàn) - 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;?// 模擬按下狀態(tài)
}
// 具體產品實現(xiàn) - 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;?// 模擬未按下狀態(tài)
}
// 抽象工廠(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);
? ??
? ??// 讀取按鈕狀態(tài)
? ??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);
};
// 具體產品實現(xiàn) - 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;?// 模擬按鈕被按下
? ? }
}
// 具體產品實現(xiàn) - 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);
? ??
? ??// 讀取輸入狀態(tài)
? ??std::cout?<<?"nReading input...n";
? ??bool?buttonState = platform.GetInputDriver().ReadButton();
? ??std::cout?<<?"Button state: "?<< (buttonState ??"PRESSED"?:?"RELEASED") <<?"n";
? ??
? ??return0;
}
2、優(yōu)缺點分析
(1)優(yōu)點
① 符合開閉原則
開閉原則?(The Open/Closed Principle, OCP) :軟件中的對象(類,模塊,函數等等)應該對于擴展是開放的,但是對于修改是封閉的。
-
- 初始支持:STM32和ESP32新增平臺:添加Nordic nRF52支持
-
- 新增
-
NRF52Platform
-
-
- 工廠類新增nRF52顯示/輸入驅動
-
無需修改
-
- 現(xiàn)有STM32/ESP32代碼
② 接口統(tǒng)一化
// 統(tǒng)一的硬件操作接口
platform->display.Init();
platform->input.Init();
platform->display.Draw(10, 20);
platform->display.Draw(30, 40);
bool buttonState = platform->input.ReadButton();
③ 平臺無關性設計
- 業(yè)務邏輯與硬件解耦代碼復用率高,減少平臺移植工作量(新平臺只需實現(xiàn)具體工廠和產品)
(2)缺點
① 擴展新產品困難
例如:
- 初始設計:顯示+輸入新增需求:攝像頭模塊修改成本:
-
- 修改所有3個平臺工廠類添加3個攝像頭驅動實現(xiàn)更新所有測試用例