(仅供自己学习记录)

一、C++ 设计模式总览

设计模式是软件设计中反复出现的问题的通用解决方案,分为三大类,总计23 种经典设计模式,C++ 完全支持所有模式:

1. 创建型模式(5 种)

负责对象的创建 / 初始化,解耦对象创建与使用(关注如何创建对象,使系统不依赖于对象创建的具体方式。这在嵌入式系统中对管理稀缺、唯一的硬件资源至关重要)

  • 单例模式、工厂模式、抽象工厂、建造者模式、原型模式

2. 结构型模式(7 种)

负责类 / 对象的组合,简化系统结构(关注如何组合类或对象以形成更大、更灵活的结构。在嵌入式里,常用于统一和适配各类硬件接口。)

  • 适配器、桥接、装饰、外观、享元、组合、代理模式

3. 行为型模式(11 种)

负责对象间的通信、职责分配(关注对象间的职责分配和通信。嵌入式系统本质上是事件驱动和状态驱动的,这类模式能极大提升响应能力和逻辑清晰度)

  • 观察者、策略、命令、责任链、状态、迭代器、模板、中介、访问者、备忘录、解释器方法

二、嵌入式开发最常用的 5 种设计模式

嵌入式场景核心需求:资源有限、低耦合、可移植、硬件抽象、单实例资源管理。排名优先级:单例 > 工厂 > 适配器 > 观察者 > 状态模式

1. 单例模式

核心作用:保证一个类只有一个实例,提供全局唯一访问点,用于管理:串口、Flash、I2C、SPI、电源、配置管理器等独占硬件资源

嵌入式特点:硬件资源唯一,不能重复初始化,避免资源冲突。

嵌入式场景

  • 唯一硬件资源管理:如UART、SPI控制器、系统时钟、全局配置管理器等。确保不会出现多个驱动实例争用同一硬件。

  • 关键实现要点:在嵌入式(尤其无RTOS环境)中,为规避动态分配和线程安全问题,可使用静态局部变量​ 或在程序启动时显式初始化的全局静态实例来实现。避免getInstance()内的懒汉式检查(除非有同步机制)。

#include <iostream>
// 嵌入式禁用异常,用静态实例实现单例(线程安全,无内存泄漏)
class UartManager {
private:
    // 私有构造函数:禁止外部创建对象
    UartManager() {
        // 模拟串口初始化
        std::cout << "UART 初始化:波特率115200" << std::endl;
    }

    // 禁用拷贝和赋值
    UartManager(const UartManager&) = delete;
    UartManager& operator=(const UartManager&) = delete;

public:
    // 全局唯一获取实例接口
    static UartManager& getInstance() {
        static UartManager instance; // 静态局部变量,C++11后线程安全
        return instance;
    }

    // 业务方法:发送数据
    void sendData(const char* data) {
        std::cout << "UART发送:" << data << std::endl;
    }
};

// 嵌入式使用示例
int main() {
    // 无论调用多少次,都是同一个实例
    UartManager& uart1 = UartManager::getInstance();
    UartManager& uart2 = UartManager::getInstance();

    uart1.sendData("Hello Embedded!");
    
    // 验证是同一个对象
    if(&uart1 == &uart2) {
        std::cout << "单例验证:两个对象是同一个实例" << std::endl;
    }
    return 0;
}

2. 工厂模式(硬件抽象必备)

核心作用:解耦对象创建与使用,统一管理不同硬件驱动(传感器、屏幕、电机),嵌入式核心:驱动可插拔、易移植

嵌入式场景

  • 硬件抽象层(HAL)​ 的核心。例如,定义一个“显示器”抽象接口,由“OLED工厂”和“LCD工厂”来创建具体的驱动对象。更换显示屏时,只需更换工厂。

  • 多型号兼容:同一产品线使用不同型号的传感器(如不同厂家的温湿度传感器),通过工厂返回统一的接口,上层应用无需修改。

#include <iostream>
#include <string>

// 抽象产品:传感器接口
class Sensor {
public:
    virtual ~Sensor() = default;
    virtual void init() = 0;    // 初始化
    virtual float read() = 0;   // 读取数据
    virtual std::string getName() = 0;
};

// 具体产品1:DHT11
class DHT11 : public Sensor {
public:
    void init() override { std::cout << "DHT11 初始化完成" << std::endl; }
    float read() override { return 25.5f; }
    std::string getName() override { return "DHT11"; }
};

// 具体产品2:DHT22
class DHT22 : public Sensor {
public:
    void init() override { std::cout << "DHT22 初始化完成" << std::endl; }
    float read() override { return 26.2f; }
    std::string getName() override { return "DHT22"; }
};

// 工厂类:统一创建传感器
class SensorFactory {
public:
    enum Type { DHT11_TYPE, DHT22_TYPE };
    
    static Sensor* createSensor(Type type) {
        switch(type) {
            case DHT11_TYPE: return new DHT11();
            case DHT22_TYPE: return new DHT22();
            default: return nullptr;
        }
    }
};

// 嵌入式使用:上层代码不关心具体传感器型号
int main() {
    Sensor* sensor = SensorFactory::createSensor(SensorFactory::DHT11_TYPE);
    sensor->init();
    std::cout << sensor->getName() << " 读取温度:" << sensor->read() << "℃" << std::endl;

    delete sensor;
    return 0;
}

3. 适配器模式(硬件兼容)

核心作用:将不兼容的接口转换为统一接口,嵌入式用于:兼容新旧驱动、不同通信协议、第三方库接口适配。

例:统一 I2C 和 SPI 接口的 OLED 屏幕驱动。

嵌入式场景

  • 新旧驱动兼容:新换的传感器API与旧的不兼容?写一个适配器,让新传感器看起来像旧的。

  • 接口标准化:将不同供应商的、接口各异的模块(如Wi-Fi、蓝牙模块)适配成统一的“网络接口”。

  • 协议转换:在软件层面模拟一个硬件接口(例如,用GPIO模拟I2C时序时,该模拟层就是一个适配器)。

#include <iostream>

// 目标接口:统一屏幕接口
class Screen {
public:
    virtual void display(const char* data) = 0;
};

// 适配者1:旧I2C屏幕(不兼容接口)
class I2COled {
public:
    void i2cSend(const char* data) {
        std::cout << "[I2C屏幕] 显示:" << data << std::endl;
    }
};

// 适配者2:新SPI屏幕(不兼容接口)
class SPIOled {
public:
    void spiWrite(const char* data) {
        std::cout << "[SPI屏幕] 显示:" << data << std::endl;
    }
};

// 适配器:I2C转统一接口
class I2CAdapter : public Screen {
private:
    I2COled* oled;
public:
    I2CAdapter(I2COled* o) : oled(o) {}
    void display(const char* data) override {
        oled->i2cSend(data); // 转换接口
    }
};

// 适配器:SPI转统一接口
class SPIAdapter : public Screen {
private:
    SPIOled* oled;
public:
    SPIAdapter(SPIOled* o) : oled(o) {}
    void display(const char* data) override {
        oled->spiWrite(data); // 转换接口
    }
};

// 上层统一调用
int main() {
    I2COled i2cScreen;
    SPIOled spiScreen;

    Screen* screen1 = new I2CAdapter(&i2cScreen);
    Screen* screen2 = new SPIAdapter(&spiScreen);

    screen1->display("系统启动");
    screen2->display("温度正常");

    delete screen1;
    delete screen2;
    return 0;
}

4. 观察者模式(事件驱动首选)

核心作用:一对多通知,一个对象状态变化,自动通知所有依赖对象。

例:按键事件、报警通知、数据更新、中断回调(嵌入式事件驱动核心模式)。

嵌入式场景

  • 事件/消息系统:硬件中断(如按键按下、定时器到点)产生一个事件,多个任务(如屏幕更新、数据记录)作为观察者监听并处理。

  • 数据发布-订阅:传感器数据采集完成后,作为“主题”通知多个“观察者”(如滤波算法、显示模块、通信模块)。

  • 与硬件中断的映射:这是将硬件的事件驱动特性映射到软件逻辑的绝佳范式。

#include <iostream>
#include <vector>

// 抽象观察者
class Observer {
public:
    virtual void update(float temp) = 0;
};

// 主题:温度传感器
class TempSensor {
private:
    std::vector<Observer*> observers;
    float temp;
public:
    // 添加观察者
    void addObserver(Observer* obs) {
        observers.push_back(obs);
    }

    // 设置温度并通知所有观察者
    void setTemp(float t) {
        temp = t;
        notify();
    }

    // 通知所有观察者
    void notify() {
        for(auto obs : observers) {
            obs->update(temp);
        }
    }
};

// 具体观察者:屏幕显示
class Screen : public Observer {
public:
    void update(float temp) override {
        std::cout << "[屏幕] 当前温度:" << temp << "℃" << std::endl;
    }
};

// 具体观察者:蜂鸣器报警
class Buzzer : public Observer {
public:
    void update(float temp) override {
        if(temp > 30) {
            std::cout << "[蜂鸣器] 温度过高!报警!" << std::endl;
        }
    }
};

// 嵌入式使用:温度变化自动通知屏幕和蜂鸣器
int main() {
    TempSensor sensor;
    Screen screen;
    Buzzer buzzer;

    sensor.addObserver(&screen);
    sensor.addObserver(&buzzer);

    sensor.setTemp(28.0f);
    sensor.setTemp(35.0f);
    return 0;
}

5. 状态模式(设备状态管理)

核心作用:管理对象的状态切换,替代大量if-else/switch,让状态逻辑清晰。

例:电机状态(停止 / 运行 / 故障)、设备开关机、充电状态。

嵌入式场景

  • 设备状态机:如充电管理(空闲、充电、充满、故障)、通信模块(初始化、寻网、连接、传输、睡眠)、电机控制(停止、正转、反转、刹车)。

  • 优势:彻底消除庞大的switch-caseif-else语句,将每个状态的行为封装在独立的类中,使状态转换逻辑清晰,易于扩展新状态。

#include <iostream>

// 前置声明
class Machine;

// 抽象状态
class State {
public:
    virtual void handle(Machine* machine) = 0;
};

// 机器类
class Machine {
private:
    State* currentState;
public:
    Machine(State* initState) : currentState(initState) {}
    void setState(State* s) { currentState = s; }
    void run() { currentState->handle(this); }
};

// 具体状态1:停止
class StopState : public State {
public:
    void handle(Machine* machine) override;
};

// 具体状态2:运行
class RunState : public State {
public:
    void handle(Machine* machine) override;
};

// 状态实现
void StopState::handle(Machine* machine) {
    std::cout << "电机:停止状态 → 切换为运行" << std::endl;
    machine->setState(new RunState());
}

void RunState::handle(Machine* machine) {
    std::cout << "电机:运行状态 → 切换为停止" << std::endl;
    machine->setState(new StopState());
}

// 嵌入式使用:状态自动切换
int main() {
    Machine machine(new StopState());
    machine.run(); // 停止→运行
    machine.run(); // 运行→停止
    return 0;
}

三、嵌入式设计模式总结

1. 23 种设计模式分类

  • 创建型:管理对象创建
  • 结构型:管理类 / 对象组合
  • 行为型:管理对象通信

2. 嵌入式掌握 5 种(优先级排序)

  1. 单例模式:管理唯一硬件资源(串口、Flash、I2C)
  2. 工厂模式:抽象硬件驱动,兼容多设备
  3. 适配器模式:统一不兼容的硬件接口
  4. 观察者模式:事件驱动、中断通知
  5. 状态模式:设备状态机管理

3. 嵌入式使用原则

  • 优先用简单模式,拒绝过度设计
  • 内存优先:少用动态分配,推荐静态实例
  • 面向硬件:所有模式服务于驱动解耦、资源安全、可移植
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐