星标公众号,让嵌入式知识 “投喂” 不停歇!

一、抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口用于创建相关或依赖对象的家族,而无需指定它们的具体类。

在需要高兼容性的嵌入式系统中,抽象工厂模式能显著降低多平台适配成本,确保硬件组件间的兼容性,是构建可移植嵌入式框架的核心模式

上一篇我们分享了:嵌入式编程模型 | 简单工厂模式

抽象工厂模式(Abstract Factory Pattern)与简单工厂模式有很多相似之处。我们先做个对比:

特性 简单工厂模式 抽象工厂模式
嵌入式应用
单一设备驱动管理
整套硬件平台适配
适用场景
产品类型少且变化不频繁
需要创建多个相关产品的系统
抽象级别
产品级别抽象
工厂级别抽象
创建对象
单个产品
产品家族(多个相关产品)
工厂类型
单一工厂类
抽象工厂+多个具体工厂实现类
扩展性
添加新产品需修改工厂类
添加新产品族只需新增工厂类

简单工厂模式适用于产品类型少且变化不频繁的场景。如嵌入式中对单一设备驱动管理,比如管理LCD驱动:

抽象工厂模式适用于需要创建多个相关产品的系统。如嵌入式中对整个硬件平台的管理。

抽象工厂模式的核心包含四个关键部分:

  1. 抽象工厂(Abstract Factory):声明创建一系列产品的方法。
  2. 具体工厂(Concrete Factory):实现抽象工厂的方法,创建具体产品。
  3. 抽象产品(Abstract Product):声明产品接口。
  4. 具体产品(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(1020);
    platform->display.Draw(3040);
    
    // 读取按钮状态
    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(510);
    platform.GetDisplayDriver().Draw(1525);
    platform.GetDisplayDriver().Draw(3040);
    
    // 读取输入状态
    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驱动)时 → 简单工厂
  • 当需要管理相关产品族(如整套硬件驱动)时 → 抽象工厂