Embedded Programming Model | Abstract Factory Pattern

Follow our official account to keep receiving embedded knowledge!

1. Abstract Factory Pattern

The Abstract Factory Pattern is a creational design pattern that provides an interface for creating a family of related or dependent objects without specifying their concrete classes.

In embedded systems that require high compatibility, the Abstract Factory Pattern can significantly reduce the cost of multi-platform adaptation and ensure compatibility between hardware components, making it a core pattern for building portable embedded frameworks.

In the previous article, we shared: Embedded Programming Model | Simple Factory Pattern

The Abstract Factory Pattern has many similarities with the Simple Factory Pattern. Let’s compare them:

Feature Simple Factory Pattern Abstract Factory Pattern
Embedded Application Single device driver management Whole hardware platform adaptation
Applicable Scenarios Few product types with infrequent changes Systems that need to create multiple related products
Level of Abstraction Product-level abstraction Factory-level abstraction
Object Creation Single product Product family (multiple related products)
Factory Type Single factory class Abstract factory + multiple concrete factory implementation classes
Extensibility Adding new products requires modifying the factory class Adding a new product family only requires adding a new factory class

The Simple Factory Pattern is suitable for scenarios with few product types and infrequent changes. For example, in embedded systems for managing a single device driver, such as an LCD driver:

Embedded Programming Model | Abstract Factory Pattern

The Abstract Factory Pattern is suitable for systems that need to create multiple related products, such as managing an entire hardware platform in embedded systems.

The core of the Abstract Factory Pattern includes four key components:

  1. Abstract Factory: Declares methods for creating a series of products.
  2. Concrete Factory: Implements the methods of the abstract factory to create concrete products.
  3. Abstract Product: Declares the product interface.
  4. Concrete Product: Implements the abstract product interface, defining the concrete product.

Among these, points 2 to 4 are the key points of the Simple Factory Pattern. The Simple Factory Pattern combined with point 1, the abstract factory, constitutes the Abstract Factory Pattern.

2. Embedded: Multi-Platform Hardware Abstraction Layer

Embedded Programming Model | Abstract Factory Pattern

Devices need to support different platforms (STM32/ESP32, etc.), with compatible input and output drivers for each platform.

1. Code

C Language Version:

#include <stdio.h>
#include <stdbool.h>

// Abstract Product
typedef struct {
    void (*Init)(void);
    void (*Draw)(int x, int y);
} DisplayDriver;

typedef struct {
    void (*Init)(void);
    bool (*ReadButton)(void);
} InputDriver;

// Concrete Product Implementation - STM32 Platform
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");
    return true; // Simulate button pressed state
}

// Concrete Product Implementation - ESP32 Platform
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");
    return false; // Simulate button not pressed state
}

// Abstract Factory
typedef struct {
    DisplayDriver display;
    InputDriver input;
} HWPlatform;

// Concrete Factory - STM32 Platform
const HWPlatform stm32_platform = {
    {stm32_disp_init, stm32_draw},
    {stm32_button_init, stm32_read_button}
};

// Concrete Factory - ESP32 Platform
const HWPlatform esp32_platform = {
    {esp32_disp_init, esp32_draw},
    {esp32_button_init, esp32_read_button}
};

int main() {
    // Select platform - define USE_STM32 macro to choose
    #if defined(USE_STM32)
        const char* platform_name = "STM32";
        const HWPlatform* platform = &stm32_platform;
    #else
        const char* platform_name = "ESP32";
        const HWPlatform* platform = &esp32_platform;
    #endif
    
    printf("Running on %s platform\n", platform_name);
    
    // Initialize display
    platform->display.Init();
    
    // Initialize input
    platform->input.Init();
    
    // Draw graphics
    platform->display.Draw(10, 20);
    platform->display.Draw(30, 40);
    
    // Read button state
    bool buttonState = platform->input.ReadButton();
    printf("Button state: %s\n", buttonState ? "PRESSED" : "RELEASED");
    
    return 0;
}
Embedded Programming Model | Abstract Factory Pattern

C++ Version:

#include <iostream>
#include <cstdint>

// Abstract Product Interface
struct DisplayDriver {
    void (*Init)(void);
    void (*Draw)(int x, int y);
};

struct InputDriver {
    void (*Init)(void);
    bool (*ReadButton)(void);
};

// Concrete Product Implementation - STM32 Platform
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";
        return true; // Simulate button pressed
    }
}

// Concrete Product Implementation - ESP32 Platform
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";
        return false; // Simulate button not pressed
    }
}

// Abstract Factory
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;
};

// Concrete Factory - STM32 Platform
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";
    }
};

// Concrete Factory - ESP32 Platform
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";
    }
};

// Usage Example
int main() {
    std::cout << "=== Embedded Hardware Platform Demo ===\n";
    
    // Platform selection
    #if defined(USE_STM32)
        STM32Platform platform;
        std::cout << "Selected platform: STM32\n";
    #else
        ESP32Platform platform;
        std::cout << "Selected platform: ESP32\n";
    #endif

    // Print platform information
    platform.PrintPlatformInfo();
    
    // Initialize hardware
    std::cout << "\nInitializing hardware...\n";
    platform.GetDisplayDriver().Init();
    platform.GetInputDriver().Init();
    
    // Use display driver
    std::cout << "\nDrawing on display...\n";
    platform.GetDisplayDriver().Draw(5, 10);
    platform.GetDisplayDriver().Draw(15, 25);
    platform.GetDisplayDriver().Draw(30, 40);
    
    // Read input state
    std::cout << "\nReading input...\n";
    bool buttonState = platform.GetInputDriver().ReadButton();
    std::cout << "Button state: " << (buttonState ? "PRESSED" : "RELEASED") << "\n";
    
    return 0;
}
Embedded Programming Model | Abstract Factory Pattern

2. Advantages and Disadvantages Analysis

(1) Advantages

① Complies with the Open/Closed Principle

Embedded Programming Model | Abstract Factory Pattern

Open/Closed Principle (OCP): Objects (classes, modules, functions, etc.) in software should be open for extension but closed for modification.

  • Initial support: STM32 and ESP32
  • New platform: Add support for Nordic nRF52
    • Add<span>NRF52Platform</span> factory class
    • Add nRF52 display/input drivers
    • No need to modify existing STM32/ESP32 code

② Unified Interface

// Unified hardware operation interface
platform->display.Init();
platform->input.Init();
platform->display.Draw(10, 20);
platform->display.Draw(30, 40);
bool buttonState = platform->input.ReadButton();

③ Platform-Independent Design

  • Decouples business logic from hardware
  • High code reuse rate, reducing platform porting workload (new platforms only need to implement concrete factories and products)

(2) Disadvantages

① Difficult to Extend New Products

Embedded Programming Model | Abstract Factory Pattern

For example:

  • Initial design: Display + Input
  • New requirement: Camera module
  • Modification cost:
    • Modify all 3 platform factory classes
    • Add 3 camera driver implementations
    • Update all test cases

3. Conclusion: When to Choose Abstract Factory

Embedded Programming Model | Abstract Factory Pattern
  • When managing asingle product type (e.g., LCD driver) → Simple Factory
  • When managingrelated product families (e.g., a complete set of hardware drivers) → Abstract Factory

You might also like:

Embedded Programming Model | Simple Factory Pattern

Embedded Software: Functional vs Non-Functional Programming

Embedded Field: The Ultimate Showdown between Linux and RTOS!

Embedded Device Networking: From Basics to Practice!

Embedded Performance Metrics Hide These Secrets, How Many Do You Know?

Embedded Software Advanced Guide, Let’s Level Up Together!

Embedded Programming Model | MVC Model

Embedded Programming Model | Observer Pattern

Step-by-step guide to building an embedded containerized development environment!

An elegant multi-functional embedded debugger!

A very lightweight embedded logging library!

A very lightweight embedded thread pool library!

Popular C language projects on GitHub!

Practical | Teach you to light up a lamp through a webpage in 10 minutes

Essential Skills for Embedded Development | Git Submodules

Leave a Comment