嵌入式Linux C++常用设计模式
1. 前言
1.1 为什么嵌入式需要设计模式?
嵌入式开发面临硬件唯一、驱动迭代频繁、状态复杂、异步事件多、资源受限等问题,设计模式并非单纯的代码技巧,而是解耦硬件与业务、简化复杂逻辑、提升代码可移植性的工程化方案,让嵌入式代码从能用走向好用、易维护。
| 嵌入式痛点 | 设计模式的解决思路 |
|---|---|
| 硬件资源唯一(如I2C控制器、串口) | 单例模式确保资源唯一控制,避免竞争冲突 |
| 硬件/驱动迭代频繁(更换传感器/芯片) | 适配器/工厂模式解耦业务与底层,最小化代码修改 |
| 系统状态复杂(待机/工作/故障/低功耗) | 状态模式替代臃肿switch-case,提升可读性与扩展性 |
| 异步事件多(按键中断、传感器报警、IO触发) | 观察者模式实现解耦的事件驱动,简化异步逻辑 |
| 内存/算力受限(嵌入式通用痛点) | 轻量级设计模式规范代码结构,避免冗余与内存泄漏 |
1.2 设计模式三原则
设计模式的核心是基于原则的灵活运用,以下为设计模式三原则:
| 原则 | 核心定义 | 示例 |
|---|---|---|
| 单一职责 | 一个类只负责一个独立的功能模块,不做跨界操作 | I2C驱动类仅处理总线读写,不包含传感器数据解析;传感器类仅处理数据采集,不包含业务逻辑 |
| 开闭原则 | 对扩展开放 ,对修改关闭 | 新增传感器时,继承基类实现新方法,不修改原有驱动框架;新增通信协议时,新增策略类,不修改通信管理器 |
| 依赖倒置 | 面向接口/抽象类编程,而非具体实现 | 定义Sensor抽象接口,所有传感器驱动实现该接口,业务层仅调用接口方法,不依赖具体传感器 |
2. 创建型模式(核心解决:谁来初始化硬件/资源?)
创建型模式的核心是控制对象的创建方式 ,适配嵌入式中"硬件资源唯一、外设类型多变"的特点,重点掌握单例模式(饿汉+懒汉)、工厂模式(简单+工厂方法)。
2.1 单例模式
核心介绍
嵌入式中大量物理资源是全局唯一 的(如I2C/SPI控制器、系统日志器、配置管理器),单例模式确保一个类在程序生命周期内仅创建一个实例,避免多实例操作硬件导致的资源竞争、寄存器混乱等问题。
单例分为两种核心实现,核心差异是初始化时机,需根据资源类型选择,同时重点关注:线程安全、内存占用、ISR(中断服务函数)兼容性。
- 饿汉式:程序启动时立即初始化实例(空间换时间)
- 懒汉式:首次使用时才初始化实例(时间换空间)

(1)饿汉式单例
核心特性
程序启动时(类加载阶段)完成实例初始化,天生线程安全 (无需加锁),但会提前占用静态内存;支持在ISR中调用 (无锁操作,不会导致中断阻塞),适合启动后必须立即初始化的核心硬件资源(如系统日志器、核心总线控制器、中断管理器)。
代码示例(饿汉式I2C控制器)
cpp
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
// 嵌入式I2C控制器(饿汉式单例)
class I2CController_Hungry {
private:
// 1. 私有构造函数:禁止外部实例化
I2CController_Hungry() {
// 打开I2C设备节点、初始化总线参数
fd_ = open("/dev/i2c-1", O_RDWR);
if (fd_ < 0) {
std::cerr << "[饿汉式] I2C控制器初始化失败" << std::endl;
return;
}
std::cout << "[饿汉式] I2C控制器初始化成功(程序启动时执行)" << std::endl;
}
// 2. 禁用拷贝/赋值运算符:防止多实例创建
I2CController_Hungry(const I2CController_Hungry&) = delete;
I2CController_Hungry& operator=(const I2CController_Hungry&) = delete;
// 3. 静态成员实例:程序启动时直接初始化(饿汉式核心)
static I2CController_Hungry instance_;
int fd_; // I2C设备文件描述符(嵌入式核心资源)
public:
// 4. 公有静态方法:获取全局唯一实例(无锁,天生线程安全)
static I2CController_Hungry& getInstance() {
return instance_;
}
// I2C总线写操作(寄存器+数据)
bool writeReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) {
if (fd_ < 0) return false;
// 通过ioctl实现I2C写(此处简化)
std::cout << "[饿汉式] I2C写:设备0x" << std::hex << (int)dev_addr
<< " 寄存器0x" << (int)reg_addr << " 数据0x" << (int)data << std::dec << std::endl;
return true;
}
// 5. 资源释放方法
static void release() {
if (instance_.fd_ >= 0) {
close(instance_.fd_);
instance_.fd_ = -1;
std::cout << "[饿汉式] I2C控制器资源释放完成" << std::endl;
}
}
};
// 静态实例初始化:程序启动时执行(饿汉式核心,必须写在类外)
I2CController_Hungry I2CController_Hungry::instance_;
// 饿汉式单例测试函数
void test_hungry_singleton() {
std::cout << "\n===== 饿汉式单例测试 =====" << std::endl;
// 获取实例(多次调用返回同一个)
I2CController_Hungry& i2c1 = I2CController_Hungry::getInstance();
i2c1.writeReg(0x48, 0x00, 0x20); // 操作温湿度传感器
I2CController_Hungry& i2c2 = I2CController_Hungry::getInstance();
std::cout << "是否为同一实例:" << (&i2c1 == &i2c2) << std::endl; // 输出1(真)
I2CController_Hungry::release();
}
(2)懒汉式单例
核心特性
首次调用getInstance()时才完成实例初始化,按需占用内存 (嵌入式内存受限场景的首选),但需手动实现线程安全(双重检查锁);不支持在ISR中调用 (加锁操作会导致中断阻塞),适合非启动必用的非核心资源(如传感器控制器、扩展模块、日志辅助器)。
代码示例(线程安全懒汉式I2C控制器)
cpp
#include <iostream>
#include <mutex>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
// 嵌入式I2C控制器(懒汉式单例,线程安全)
class I2CController_Lazy {
private:
// 1. 私有构造函数
I2CController_Lazy() {
fd_ = open("/dev/i2c-1", O_RDWR);
if (fd_ < 0) {
std::cerr << "[懒汉式] I2C控制器初始化失败" << std::endl;
return;
}
std::cout << "[懒汉式] I2C控制器初始化成功(首次使用时执行)" << std::endl;
}
// 2. 禁用拷贝/赋值
I2CController_Lazy(const I2CController_Lazy&) = delete;
I2CController_Lazy& operator=(const I2CController_Lazy&) = delete;
// 3. 静态实例指针:初始为nullptr(懒汉式核心)
static I2CController_Lazy* instance_;
// 4. 静态互斥锁:保证线程安全
static std::mutex mutex_;
int fd_;
public:
// 5. 公有静态方法:获取实例(双重检查锁,线程安全+低开销)
static I2CController_Lazy* getInstance() {
if (instance_ == nullptr) { // 第一层检查:减少锁竞争(99%场景直接跳过锁)
std::lock_guard<std::mutex> lock(mutex_); // 加锁:保证多线程安全
if (instance_ == nullptr) { // 第二层检查:确保唯一实例
instance_ = new I2CController_Lazy();
}
}
return instance_;
}
// I2C总线写操作
bool writeReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) {
if (fd_ < 0 || instance_ == nullptr) return false;
std::cout << "[懒汉式] I2C写:设备0x" << std::hex << (int)dev_addr
<< " 寄存器0x" << (int)reg_addr << " 数据0x" << (int)data << std::dec << std::endl;
return true;
}
// 6. 资源释放
static void release() {
std::lock_guard<std::mutex> lock(mutex_); // 加锁释放,避免多线程冲突
if (instance_ != nullptr) {
if (instance_->fd_ >= 0) close(instance_->fd_);
delete instance_;
instance_ = nullptr; // 重置为nullptr,支持重新初始化
std::cout << "[懒汉式] I2C控制器资源释放完成" << std::endl;
}
}
};
// 静态成员初始化:初始为nullptr(懒汉式核心)
I2CController_Lazy* I2CController_Lazy::instance_ = nullptr;
std::mutex I2CController_Lazy::mutex_;
// 懒汉式单例测试函数
void test_lazy_singleton() {
std::cout << "\n===== 懒汉式单例测试 =====" << std::endl;
// 首次调用:初始化实例
I2CController_Lazy* i2c1 = I2CController_Lazy::getInstance();
i2c1->writeReg(0x48, 0x01, 0x30);
// 再次调用:直接返回已有实例
I2CController_Lazy* i2c2 = I2CController_Lazy::getInstance();
std::cout << "是否为同一实例:" << (i2c1 == i2c2) << std::endl; // 输出1(真)
I2CController_Lazy::release();
}
(3)饿汉式 vs 懒汉式
| 特性 | 饿汉式单例 | 懒汉式单例(线程安全版) |
|---|---|---|
| 初始化时机 | 程序启动时(类加载阶段) | 首次调用getInstance()时 |
| 线程安全 | 安全(无需锁,无开销) | 需双重检查锁(C++11+支持,低开销) |
| 内存占用 | 启动即占静态内存 | 按需占用(嵌入式内存受限首选) |
| ISR兼容性 | 完全支持(无锁,不阻塞中断) | 完全不支持(锁会导致系统崩溃) |
| 实现复杂度 | 简单(无额外逻辑) | 中等(双重检查锁+锁管理) |
| 资源释放 | 静态实例,仅需释放硬件资源 | 动态实例,需释放内存+硬件资源 |
| 嵌入式适用场景 | 核心资源:系统日志、核心总线、中断管理器 | 非核心资源:传感器、扩展模块、辅助工具 |
| 核心优点 | 简单、无锁开销、ISR兼容、启动即可用 | 节省内存、按需初始化、支持重新初始化 |
| 核心缺点 | 浪费内存、启动时间略长 | 实现复杂、不支持ISR、有轻微锁开销 |
(4)实践选择
- 资源分级选择:核心硬件用饿汉式,非核心硬件用懒汉式,兼顾性能与内存;
- 必须手动释放 :嵌入式无垃圾回收(GC),所有单例必须实现
release()方法,释放文件句柄、寄存器、内存等资源; - 禁用ISR调用懒汉式:中断服务函数中禁止调用加锁的代码,否则会导致系统死锁;
- C++11+必选:保证双重检查锁的内存模型原子性,避免多线程下的半初始化问题;
- 禁用裸指针传递 :懒汉式返回指针后,避免外部手动delete,统一通过
release()释放。
2.2 工厂模式
核心介绍
嵌入式开发中常遇到同类型外设多型号适配 的问题(如同时兼容DHT11/BME280温湿度传感器、WiFi/LoRa/4G通信模块),工厂模式将对象的创建逻辑与业务逻辑解耦,业务层无需关注具体硬件的实现细节,仅通过工厂获取实例即可。
工厂模式分为两个层级,核心差异是是否符合开闭原则 ,需根据嵌入式系统的规模和扩展需求选择:
- 简单工厂模式:一个工厂类创建所有类型实例(简单但违反开闭原则);
- 工厂方法模式:一个产品类型对应一个工厂类(符合开闭原则,扩展友好)。

(1)简单工厂模式
核心特性
由一个工厂类 根据传入的参数(类型字符串/枚举)创建不同的产品实例,代码极简、类数量少、内存占用低,适合传感器/外设类型固定、无需频繁扩展 的小规模嵌入式系统。
缺点是违反开闭原则 :新增产品时需修改工厂类的create方法,适合产品类型稳定的场景。
代码示例(传感器简单工厂)
cpp
#include <iostream>
#include <string>
#include <memory>
#include <stdint.h>
// 抽象产品:传感器接口(依赖倒置原则,所有传感器实现此接口)
class Sensor {
public:
virtual ~Sensor() = default;
virtual float readData() = 0; // 纯虚函数:统一数据读取接口
virtual std::string getType() = 0; // 纯虚函数:获取传感器类型
};
// 具体产品1:温湿度传感器(DHT11)
class TemperatureSensor : public Sensor {
public:
float readData() override {
// 从I2C/SPI读取数据(此处模拟)
return 25.6f; // 单位:℃
}
std::string getType() override {
return "TemperatureSensor(DHT11)";
}
};
// 具体产品2:光照传感器(BH1750)
class LightSensor : public Sensor {
public:
float readData() override {
return 800.5f; // 单位:lux(勒克斯)
}
std::string getType() override {
return "LightSensor(BH1750)";
}
};
// 简单工厂类:核心(一个工厂创建所有传感器实例)
class SensorSimpleFactory {
public:
// 静态创建方法:根据类型创建实例,返回智能指针
static std::unique_ptr<Sensor> createSensor(const std::string& type) {
if (type == "temp") {
return std::make_unique<TemperatureSensor>();
} else if (type == "light") {
return std::make_unique<LightSensor>();
} else {
std::cerr << "[简单工厂] 不支持的传感器类型:" << type << std::endl;
return nullptr;
}
}
};
// 简单工厂测试函数
void test_simple_factory() {
std::cout << "\n===== 简单工厂模式测试 =====" << std::endl;
// 从配置文件/硬件ID读取传感器类型
std::string sensor_type = "temp";
auto sensor1 = SensorSimpleFactory::createSensor(sensor_type);
if (sensor1) {
std::cout << "传感器类型:" << sensor1->getType()
<< " 读取值:" << sensor1->readData() << "℃" << std::endl;
}
sensor_type = "light";
auto sensor2 = SensorSimpleFactory::createSensor(sensor_type);
if (sensor2) {
std::cout << "传感器类型:" << sensor2->getType()
<< " 读取值:" << sensor2->readData() << "lux" << std::endl;
}
// 新增传感器需修改工厂类(违反开闭原则)
// auto sensor3 = SensorSimpleFactory::createSensor("pressure"); // 返回nullptr
}
(2)工厂方法模式
核心特性
将工厂抽象为接口 ,定义创建产品的纯虚方法,每个产品类型对应一个具体工厂类 ,新增产品时仅需新增"产品类+工厂类",无需修改原有代码,完全符合开闭原则 ,适合传感器/外设类型需频繁扩展 的中大规模嵌入式系统(如工业网关、智能硬件平台、车载终端)。
缺点是类数量会随产品类型增加而增多,需注意内存占用,但相较于扩展带来的维护成本,此缺点可忽略。
代码示例(传感器工厂方法)
cpp
#include <iostream>
#include <string>
#include <memory>
#include <stdint.h>
// 复用抽象产品:Sensor接口(与简单工厂一致)
class Sensor {
public:
virtual ~Sensor() = default;
virtual float readData() = 0;
virtual std::string getType() = 0;
};
// 复用具体产品:TemperatureSensor/LightSensor(与简单工厂一致)
class TemperatureSensor : public Sensor {
public:
float readData() override { return 25.6f; }
std::string getType() override { return "TemperatureSensor(DHT11)"; }
};
class LightSensor : public Sensor {
public:
float readData() override { return 800.5f; }
std::string getType() override { return "LightSensor(BH1750)"; }
};
// 抽象工厂:传感器工厂接口(工厂方法核心,定义创建产品的纯虚方法)
class SensorFactory {
public:
virtual ~SensorFactory() = default;
virtual std::unique_ptr<Sensor> createSensor() = 0; // 工厂方法
};
// 具体工厂1:温湿度传感器工厂(对应TemperatureSensor)
class TemperatureSensorFactory : public SensorFactory {
public:
std::unique_ptr<Sensor> createSensor() override {
return std::make_unique<TemperatureSensor>();
}
};
// 具体工厂2:光照传感器工厂(对应LightSensor)
class LightSensorFactory : public SensorFactory {
public:
std::unique_ptr<Sensor> createSensor() override {
return std::make_unique<LightSensor>();
}
};
// 新增产品:气压传感器(BMP280)------ 无需修改原有代码(符合开闭原则)
class PressureSensor : public Sensor {
public:
float readData() override { return 101.325f; } // 单位:kPa
std::string getType() override { return "PressureSensor(BMP280)"; }
};
// 新增工厂:气压传感器工厂 ------ 仅新增类,无任何修改操作
class PressureSensorFactory : public SensorFactory {
public:
std::unique_ptr<Sensor> createSensor() override {
return std::make_unique<PressureSensor>();
}
};
// 工厂方法测试函数
void test_factory_method() {
std::cout << "\n===== 工厂方法模式测试 =====" << std::endl;
// 方式1:直接创建具体工厂(嵌入式真实场景:从配置动态选择)
std::unique_ptr<SensorFactory> temp_factory = std::make_unique<TemperatureSensorFactory>();
auto temp_sensor = temp_factory->createSensor();
std::cout << "传感器类型:" << temp_sensor->getType()
<< " 读取值:" << temp_sensor->readData() << "℃" << std::endl;
// 方式2:新增气压传感器(仅新增类,原有代码无任何修改)
std::unique_ptr<SensorFactory> pressure_factory = std::make_unique<PressureSensorFactory>();
auto pressure_sensor = pressure_factory->createSensor();
std::cout << "传感器类型:" << pressure_sensor->getType()
<< " 读取值:" << pressure_sensor->readData() << "kPa" << std::endl;
}
(3)简单工厂 vs 工厂方法
| 特性 | 简单工厂模式 | 工厂方法模式 |
|---|---|---|
| 核心结构 | 1个工厂类 + N个产品类 | 1个抽象工厂 + N个具体工厂 + N个产品类 |
| 开闭原则 | 违反(新增产品需修改工厂类) | 符合(新增产品仅需新增类) |
| 代码复杂度 | 极低(嵌入式入门友好) | 中等(类数量增多,逻辑清晰) |
| 内存占用 | 极低(少类,少静态资源) | 中等(多类,可接受) |
| 扩展成本 | 高(修改原有代码,易引入bug) | 低(仅新增类,无修改操作) |
| 维护成本 | 高(产品越多,工厂方法越臃肿) | 低(产品与工厂一一对应,易维护) |
| 嵌入式适用场景 | 小规模系统、产品类型固定 | 中大规模系统、产品类型需频繁扩展 |
| 核心优点 | 简单、易实现、内存占用少、开发快 | 符合开闭原则、扩展灵活、易维护、可测试 |
| 核心缺点 | 扩展需修改代码、维护成本高 | 类数量多、初始化略复杂、轻微内存增加 |
(4)嵌入式工厂模式选择
- 按系统规模选择:单片机Linux/小型设备用简单工厂,工业网关/车载终端用工厂方法;
- 结合配置文件 :将外设类型写入配置文件(如
/etc/periph.conf),程序启动时动态读取并创建工厂/产品,提升灵活性; - 优先使用智能指针 :用
std::unique_ptr管理工厂和产品实例,嵌入式避免裸指针泄漏; - 产品接口归一化:抽象产品接口的方法需足够通用,避免因产品差异导致接口臃肿;
- 平滑迁移:先通过简单工厂快速实现功能,后续若需扩展,再平滑迁移到工厂方法模式(接口完全兼容)。
3. 结构性模式(核心解决:如何组织类以适配底层接口)
结构性模式的核心是通过类的组合/继承,构建灵活的类结构 ,解决嵌入式中"底层接口不兼容、子系统逻辑复杂、跨进程访问硬件"的问题,重点掌握适配器模式、外观模式、代理模式,均为嵌入式开发中的高频使用模式。
3.1 适配器模式
核心介绍
嵌入式开发中常遇到接口不兼容的问题:第三方驱动/旧版代码的接口与当前系统的标准接口不一致、更换芯片后外设接口发生变化、使用开源库时接口与业务逻辑不匹配。
适配器模式将不兼容的接口转换为系统的标准接口 ,让原本因接口不兼容而无法协作的类能够正常工作,完全解耦业务逻辑与底层驱动,更换硬件/库时仅需修改适配器,无需修改业务代码。
嵌入式中优先使用对象适配器(组合适配者,而非继承),更灵活、更节省内存。

代码示例(串口驱动适配器)
cpp
#include <iostream>
#include <string>
#include <stdint.h>
// 目标接口:系统标准串口接口(当前系统的统一接口)
class SerialPort {
public:
virtual ~SerialPort() = default;
virtual bool openPort(int baud_rate) = 0; // 标准打开接口:仅需波特率
virtual bool sendData(const std::string& data) = 0; // 标准发送接口:字符串
};
// 适配者:第三方旧串口驱动(接口不兼容,无法直接使用)
class OldSerialDriver {
public:
// 旧接口:参数多,无返回值
void initSerial(int rate, const std::string& port, uint8_t parity) {
std::cout << "旧驱动初始化:端口" << port << " 波特率" << rate << " 校验位" << (int)parity << std::endl;
is_init_ = true;
}
// 旧接口:数据为char*+长度,与标准接口不一致
void writeData(const char* buf, int len) {
if (!is_init_) return;
std::cout << "旧驱动发送:" << std::string(buf, len) << std::endl;
}
private:
bool is_init_ = false;
};
// 适配器:将旧驱动接口适配为系统标准接口(对象适配器,组合适配者)
class SerialAdapter : public SerialPort {
public:
SerialAdapter() : old_driver_() {}
// 适配标准openPort到旧initSerial
bool openPort(int baud_rate) override {
old_driver_.initSerial(baud_rate, "/dev/ttyS0", 0); // 嵌入式串口节点,固定校验位
return true;
}
// 适配标准sendData到旧writeData
bool sendData(const std::string& data) override {
old_driver_.writeData(data.c_str(), data.length());
return true;
}
private:
OldSerialDriver old_driver_; // 组合适配者(对象适配器核心)
};
// 业务层:仅依赖标准接口,无需关注底层驱动实现
void sendDeviceConfig(SerialPort& serial) {
serial.openPort(9600);
serial.sendData("device_config: baud=9600, parity=0, data=8");
}
// 测试函数
void test_adapter() {
std::cout << "\n===== 适配器模式测试 =====" << std::endl;
SerialAdapter serial_adapter;
sendDeviceConfig(serial_adapter); // 业务层无感知,适配层完成接口转换
}
应用要点
- 优先对象适配器:通过组合实现,而非继承,避免类层级过深,节省内存;
- 隔离所有修改:第三方库/旧驱动的所有适配逻辑都放在适配器中,更换库时仅需修改适配器;
- 轻量化适配:适配器层仅做接口转换,不做复杂业务逻辑,避免影响嵌入式实时性;
- 硬件接口适配:更换芯片时,适配器层屏蔽硬件寄存器的差异,业务层无需修改。
3.2 外观模式
核心介绍
嵌入式中很多子系统逻辑复杂:网络协议栈(IP/TCP/UDP初始化)、硬件通信模块(蓝牙/WiFi的扫描/连接/发送)、文件系统(挂载/打开/读写/关闭),这些子系统包含多个相互关联的类,直接调用会让业务代码臃肿、难以维护。
外观模式为复杂的子系统提供一个统一的简单接口 ,屏蔽底层子系统的细节,业务层仅需调用外观类的方法,无需关注子系统的内部实现,大幅简化代码调用。

代码示例(网络子系统外观)
cpp
#include <iostream>
#include <string>
// 子系统1:网络协议栈初始化(复杂的底层操作)
class NetworkStack {
public:
bool init() {
std::cout << "网络协议栈初始化:IP=192.168.1.100, Gateway=192.168.1.1" << std::endl;
return true;
}
void deinit() {
std::cout << "网络协议栈资源释放" << std::endl;
}
};
// 子系统2:TCP连接管理(复杂的连接逻辑)
class TCPHandler {
public:
bool connectServer(const std::string& ip, int port) {
std::cout << "TCP连接:" << ip << ":" << port << " 连接成功" << std::endl;
return true;
}
};
// 子系统3:UDP数据发送(复杂的数据包封装)
class UDPSender {
public:
bool sendPacket(const std::string& data) {
std::cout << "UDP数据包发送:" << data << std::endl;
return true;
}
};
// 外观类:网络管理器(封装所有子系统,提供简单接口)
class NetworkManager {
public:
NetworkManager() : stack_(), tcp_(), udp_() {}
// 统一的发送方法:封装子系统的所有操作
bool sendNetworkData(const std::string& ip, int port, const std::string& data) {
if (!stack_.init()) return false;
if (!tcp_.connectServer(ip, port)) return false;
return udp_.sendPacket(data);
}
~NetworkManager() {
stack_.deinit(); // 统一释放子系统资源
}
private:
NetworkStack stack_; // 组合所有子系统
TCPHandler tcp_;
UDPSender udp_;
};
// 测试函数
void test_facade() {
std::cout << "\n===== 外观模式测试 =====" << std::endl;
NetworkManager net_mgr;
// 业务层仅需调用一个方法,无需关注子系统细节
net_mgr.sendNetworkData("192.168.1.1", 8080, "embedded_linux_data_123");
}
应用要点
- 统一资源管理:外观类统一初始化和释放子系统资源,避免嵌入式资源泄漏;
- 接口极简:外观类的方法需贴合业务逻辑,避免暴露底层子系统的细节;
- 子系统解耦:外观类是子系统与业务层的唯一交互入口,子系统内部修改不影响业务层;
- 实时性保障:外观类仅做方法转发,不做复杂逻辑,避免影响嵌入式实时性。
3.3 代理模式(IPC代理为主)
核心介绍
嵌入式Linux中多进程开发是常态:特权进程管理硬件、普通进程处理业务、应用进程提供交互,跨进程访问硬件/服务是核心需求(如通过普通进程操作硬件、通过应用进程调用特权进程的服务)。
代理模式为远程/复杂对象提供一个本地代理对象 ,屏蔽跨进程通信(IPC)的细节,业务层调用代理对象的方法,代理对象通过IPC将请求转发给真实对象,处理结果后返回给业务层,实现透明的跨进程访问。
嵌入式中常用的IPC方式:管道、共享内存、消息队列、Socket,代理模式可将这些IPC方式封装起来。

代码示例(管道IPC硬件代理)
cpp
#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
// 抽象服务:硬件服务接口(本地+远程统一接口)
class HardwareService {
public:
virtual ~HardwareService() = default;
virtual std::string getHardwareStatus() = 0; // 获取硬件状态
};
// 真实服务:硬件管理器(运行在特权子进程,直接操作硬件)
class RealHardwareService : public HardwareService {
public:
std::string getHardwareStatus() override {
// 读取CPU、内存、温度等硬件状态
return "CPU:50%, MEM:30%, TEMP:45°C, VOLTAGE:3.3V";
}
};
// 代理服务:硬件代理(运行在主进程,封装管道IPC,转发请求)
class HardwareProxy : public HardwareService {
public:
HardwareProxy() {
// 创建无名管道+子进程(嵌入式极简IPC实现)
if (pipe(pipe_fd_) < 0) {
std::cerr << "管道创建失败" << std::endl;
return;
}
pid_t pid = fork();
if (pid == 0) { // 子进程:运行真实硬件服务
close(pipe_fd_[0]); // 关闭读端
RealHardwareService real_hw;
std::string status = real_hw.getHardwareStatus();
// 将硬件状态写入管道
write(pipe_fd_[1], status.c_str(), status.length());
close(pipe_fd_[1]);
exit(0);
} else if (pid > 0) { // 主进程:作为代理
close(pipe_fd_[1]); // 关闭写端
}
}
// 代理方法:通过管道获取子进程的硬件状态
std::string getHardwareStatus() override {
if (pipe_fd_[0] < 0) return "获取硬件状态失败";
char buf[128] = {0};
read(pipe_fd_[0], buf, sizeof(buf)-1);
close(pipe_fd_[0]);
return std::string(buf);
}
private:
int pipe_fd_[2]; // 管道文件描述符(fd[0]读,fd[1]写)
};
// 测试函数
void test_proxy() {
std::cout << "\n===== 代理模式测试 =====" << std::endl;
HardwareProxy hw_proxy;
// 业务层无感知IPC,直接调用代理方法
std::cout << "硬件状态:" << hw_proxy.getHardwareStatus() << std::endl;
}
应用要点
- 屏蔽IPC细节:将管道/共享内存/D-Bus的实现封装在代理类中,业务层无需关注;
- 特权隔离:真实服务运行在特权进程,代理运行在普通进程,实现硬件访问的权限控制;
- 轻量化IPC:嵌入式优先使用管道/共享内存,避免D-Bus等重量级IPC的性能开销;
- 异常处理:代理类需处理IPC通信失败的情况,避免主进程崩溃。
4. 行为型模式(核心解决:硬件事件如何响应?任务逻辑流如何设计?)
行为型模式的核心是解决类与对象之间的交互和行为分配问题 ,适配嵌入式中异步事件多、状态机复杂、算法/协议需动态切换 的特点,重点掌握观察者模式、状态模式、策略模式,是嵌入式业务逻辑设计的核心模式。
4.1 观察者模式
核心介绍
嵌入式是事件驱动的系统:按键按下/松开、传感器阈值报警、IO口电平变化、网络数据到达,这些异步事件需要及时响应,且一个事件可能需要多个模块做出反应(如按键按下→LED亮+蜂鸣器响+数据记录)。
观察者模式实现事件源与观察者的解耦:事件源(如按键)维护一个观察者列表,当事件发生时,自动通知所有观察者,观察者接收到事件后执行自己的响应逻辑,新增/移除观察者无需修改事件源代码,符合开闭原则。

代码示例(按键事件观察者)
cpp
#include <iostream>
#include <string>
#include <vector>
#include <stdint.h>
// 抽象观察者:所有事件响应者的统一接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& event) = 0; // 事件更新方法
};
// 具体观察者1:LED灯(按键事件响应者)
class LEDObserver : public Observer {
public:
void update(const std::string& event) override {
if (event == "BUTTON_PRESS") {
std::cout << "LED:亮红灯(按键按下)" << std::endl;
} else if (event == "BUTTON_RELEASE") {
std::cout << "LED:灭灯(按键松开)" << std::endl;
}
}
};
// 具体观察者2:蜂鸣器(按键事件响应者)
class BuzzerObserver : public Observer {
public:
void update(const std::string& event) override {
if (event == "BUTTON_PRESS") {
std::cout << "蜂鸣器:短鸣一声(按键按下)" << std::endl;
}
}
};
// 主题(事件源):按键(维护观察者列表,触发事件时通知)
class ButtonSubject {
public:
// 添加观察者
void attach(Observer* obs) {
observers_.push_back(obs);
}
// 移除观察者
void detach(Observer* obs) {
for (auto it = observers_.begin(); it != observers_.end(); ++it) {
if (*it == obs) {
observers_.erase(it);
break;
}
}
}
// 通知所有观察者(事件触发核心)
void notify(const std::string& event) {
for (auto obs : observers_) {
obs->update(event);
}
}
// 模拟按键中断触发(嵌入式真实场景:ISR中调用此方法)
void onButtonPress() {
notify("BUTTON_PRESS");
}
void onButtonRelease() {
notify("BUTTON_RELEASE");
}
private:
std::vector<Observer*> observers_; // 观察者列表
};
// 测试函数
void test_observer() {
std::cout << "\n===== 观察者模式测试 =====" << std::endl;
ButtonSubject button;
LEDObserver led;
BuzzerObserver buzzer;
// 注册观察者
button.attach(&led);
button.attach(&buzzer);
// 模拟按键事件(嵌入式真实场景:中断服务函数触发)
button.onButtonPress();
button.onButtonRelease();
// 移除蜂鸣器观察者
button.detach(&buzzer);
button.onButtonPress(); // 仅LED响应
}
应用要点
- 观察者轻量化 :
update方法需极简,避免在ISR中执行耗时操作(可通过消息队列将逻辑放到主线程); - 线程安全 :多线程下通知观察者需加锁(如
std::mutex),避免数据竞争; - 避免内存泄漏:移除观察者时需从列表中删除,避免野指针;
- 事件归一化:事件类型用枚举/字符串统一,便于扩展。
4.2 状态模式
核心介绍
嵌入式设备的核心逻辑往往是一个大型状态机 :设备初始化→待机→工作→故障→复位、传感器空闲→采集→处理→休眠,传统用switch-case实现的状态机,当状态/事件增多时,代码会极度臃肿、难以维护、易引入bug。
状态模式将每个状态封装为一个独立的类 ,每个状态类实现该状态下的事件处理逻辑,上下文类(设备)维护当前状态,当事件发生时,将事件转发给当前状态类处理,状态切换由状态类自行完成,彻底替代switch-case。

代码示例(设备状态机)
cpp
#include <iostream>
#include <string>
// 前向声明上下文类
class DeviceContext;
// 抽象状态:设备状态接口(所有状态实现此接口)
class DeviceState
{
public:
virtual ~DeviceState() = default;
virtual void handleEvent(DeviceContext *context, const std::string &event) = 0;
virtual std::string getStateName() = 0;
};
// 上下文类:设备(维护当前状态,转发事件)
class DeviceContext
{
public:
DeviceContext(DeviceState *init_state);
// 切换状态(上下文仅提供状态切换接口)
void setState(DeviceState *new_state);
// 转发事件到当前状态
void handleEvent(const std::string &event);
~DeviceContext();
private:
DeviceState *current_state_;
};
// 声明所有具体状态类
class InitState;
class StandbyState;
class WorkState;
class FaultState;
// 具体状态1:初始化状态
class InitState : public DeviceState
{
public:
void handleEvent(DeviceContext *context, const std::string &event) override;
std::string getStateName() override;
};
// 具体状态2:待机状态
class StandbyState : public DeviceState
{
public:
void handleEvent(DeviceContext *context, const std::string &event) override;
std::string getStateName() override;
};
// 具体状态3:工作状态
class WorkState : public DeviceState
{
public:
void handleEvent(DeviceContext *context, const std::string &event) override;
std::string getStateName() override;
};
// 具体状态4:故障状态
class FaultState : public DeviceState
{
public:
void handleEvent(DeviceContext *context, const std::string &event) override;
std::string getStateName() override;
};
// -------------------------- 所有类声明完成后,统一实现成员函数 --------------------------
// 实现DeviceContext的成员函数
DeviceContext::DeviceContext(DeviceState *init_state)
: current_state_(init_state)
{
std::cout << "设备启动,初始状态:" << current_state_->getStateName() << std::endl;
}
void DeviceContext::setState(DeviceState *new_state)
{
delete current_state_;
current_state_ = new_state;
std::cout << "设备状态切换:" << current_state_->getStateName() << std::endl;
}
void DeviceContext::handleEvent(const std::string &event)
{
current_state_->handleEvent(this, event);
}
DeviceContext::~DeviceContext()
{
delete current_state_;
}
// 实现InitState的成员函数
void InitState::handleEvent(DeviceContext *context, const std::string &event)
{
if (event == "INIT_OK") {
context->setState(new StandbyState());
} else if (event == "INIT_FAIL") {
context->setState(new FaultState());
}
}
std::string InitState::getStateName()
{
return "初始化状态";
}
// 实现StandbyState的成员函数
void StandbyState::handleEvent(DeviceContext *context, const std::string &event)
{
if (event == "START_WORK") {
context->setState(new WorkState());
} else if (event == "POWER_OFF") {
std::cout << "设备关机" << std::endl;
}
}
std::string StandbyState::getStateName()
{
return "待机状态";
}
// 实现WorkState的成员函数
void WorkState::handleEvent(DeviceContext *context, const std::string &event)
{
if (event == "STOP_WORK") {
context->setState(new StandbyState());
} else if (event == "SENSOR_ERROR") {
context->setState(new FaultState());
}
}
std::string WorkState::getStateName()
{
return "工作状态";
}
// 实现FaultState的成员函数
void FaultState::handleEvent(DeviceContext *context, const std::string &event)
{
if (event == "RESET") {
context->setState(new InitState());
}
}
std::string FaultState::getStateName()
{
return "故障状态";
}
// 测试函数
void test_state()
{
std::cout << "\n===== 状态模式测试 =====" << std::endl;
// 设备启动,初始状态为初始化
DeviceContext device(new InitState());
// 状态流转:初始化成功→待机→工作→传感器故障→复位→初始化
device.handleEvent("INIT_OK");
device.handleEvent("START_WORK");
device.handleEvent("SENSOR_ERROR");
device.handleEvent("RESET");
}
应用要点
- 状态单一职责:每个状态类仅处理该状态下的事件,避免逻辑臃肿;
- 状态切换可控:状态切换由状态类自行完成,上下文不参与具体的状态判断;
- 内存优化:嵌入式可将状态类设计为单例(饿汉式),避免频繁创建/销毁对象;
- 故障状态必做:设备的故障状态需单独封装,处理所有异常情况,保证系统鲁棒性。
4.3 策略模式
核心介绍
嵌入式开发中常遇到算法/协议需动态切换 的问题:根据信号强度自动切换WiFi/LoRa/4G通信、根据数据量动态选择压缩算法、根据功耗要求切换采集频率,传统用if-else实现的切换逻辑,扩展时需修改原有代码,违反开闭原则。
策略模式将每个算法/协议封装为一个独立的策略类,上下文类维护当前策略,运行时可动态切换策略,新增算法/协议时仅需新增策略类,无需修改原有代码,符合开闭原则。

代码示例(通信协议动态切换)
cpp
#include <iostream>
#include <string>
#include <stdint.h>
// 抽象策略:通信策略接口(所有通信协议实现此接口)
class CommStrategy {
public:
virtual ~CommStrategy() = default;
virtual bool sendData(const std::string& data) = 0;
virtual std::string getProtocolName() = 0;
};
// 具体策略1:WiFi通信(高速短距离)
class WiFiStrategy : public CommStrategy {
public:
bool sendData(const std::string& data) override {
std::cout << "WiFi(802.11n)发送:" << data << "(速率:100Mbps,距离:100m)" << std::endl;
return true;
}
std::string getProtocolName() override { return "WiFi 802.11n"; }
};
// 具体策略2:LoRa通信(低速长距离)
class LoRaStrategy : public CommStrategy {
public:
bool sendData(const std::string& data) override {
std::cout << "LoRa(SX1278)发送:" << data << "(速率:1kbps,距离:5km)" << std::endl;
return true;
}
std::string getProtocolName() override { return "LoRa SX1278"; }
};
// 上下文类:通信管理器(维护当前策略,动态切换)
class CommContext {
public:
CommContext(CommStrategy* init_strategy) : current_strategy_(init_strategy) {
std::cout << "通信管理器启动,当前协议:" << current_strategy_->getProtocolName() << std::endl;
}
// 动态切换策略(运行时切换,核心)
void setStrategy(CommStrategy* new_strategy) {
delete current_strategy_;
current_strategy_ = new_strategy;
std::cout << "通信协议切换:" << current_strategy_->getProtocolName() << std::endl;
}
// 执行通信(转发到当前策略)
bool executeSend(const std::string& data) {
return current_strategy_->sendData(data);
}
~CommContext() {
delete current_strategy_;
}
private:
CommStrategy* current_strategy_;
};
// 测试函数
void test_strategy() {
std::cout << "\n===== 策略模式测试 =====" << std::endl;
// 初始通信协议:WiFi
CommContext comm_mgr(new WiFiStrategy());
comm_mgr.executeSend("high_speed_data_123");
// 模拟信号变弱,动态切换为LoRa(嵌入式真实场景:根据信号强度判断)
comm_mgr.setStrategy(new LoRaStrategy());
comm_mgr.executeSend("long_distance_data_456");
}
应用要点
- 策略轻量化:策略类的方法需极简,避免影响嵌入式实时性;
- 动态切换时机:根据硬件状态(如信号强度、功耗、数据量)在主线程中切换策略,避免在ISR中切换;
- 策略单例化:嵌入式可将策略类设计为单例(饿汉式),避免频繁创建/销毁对象;
- 异常处理:切换策略时需处理策略创建失败的情况,保证通信不中断。
5. 嵌入式Linux C++设计模式全维度对比表
| 模式类型 | 设计模式 | 核心思想 | 嵌入式典型应用 | 核心优点 | 核心缺点 |
|---|---|---|---|---|---|
| 创建型 | 单例(饿汉) | 启动初始化,全局唯一实例 | 核心总线、系统日志、中断管理器 | 简单、无锁、ISR兼容、启动即可用 | 浪费内存、启动时间略长 |
| 创建型 | 单例(懒汉) | 按需初始化,全局唯一实例 | 传感器、扩展模块、辅助工具 | 节省内存、按需初始化、可重初始化 | 实现复杂、不支持ISR、轻微锁开销 |
| 创建型 | 简单工厂 | 一个工厂创建所有产品 | 小规模系统、固定类型传感器 | 简单、开发快、内存占用极低 | 违反开闭原则、扩展/维护成本高 |
| 创建型 | 工厂方法 | 一个产品对应一个工厂 | 中大规模系统、可扩展外设 | 符合开闭原则、扩展灵活、易维护 | 类数量多、轻微内存增加 |
| 结构型 | 适配器模式 | 适配不兼容接口为标准接口 | 第三方驱动、旧版代码、芯片更换 | 解耦业务与底层、移植性强、修改少 | 增加类层级、轻微性能损耗 |
| 结构型 | 外观模式 | 为复杂子系统提供统一简单接口 | 网络协议栈、通信模块、文件系统 | 简化调用、屏蔽细节、统一资源管理 | 不符合单一职责、子系统修改需改外观 |
| 结构型 | 代理模式 | 为远程对象提供本地代理,屏蔽IPC | 跨进程硬件访问、特权服务调用 | 解耦进程、权限隔离、屏蔽IPC细节 | 增加通信开销、轻微延迟升高 |
| 行为型 | 观察者模式 | 事件源通知观察者,实现事件驱动 | 按键中断、传感器报警、IO触发 | 解耦事件源与响应者、扩展灵活 | 观察者过多时通知耗时、需线程安全 |
| 行为型 | 状态模式 | 用类封装状态,替代switch-case | 设备状态机、传感器工作流程 | 状态清晰、扩展灵活、易维护 | 状态类增多、轻微内存增加 |
| 行为型 | 策略模式 | 封装算法/协议,运行时动态切换 | 通信协议、压缩算法、采集频率 | 切换灵活、符合开闭原则、易扩展 | 策略类增多、需理解所有策略 |
6. 嵌入式Linux C++设计模式常见问题
单例模式专项
- 问 :嵌入式Linux中,饿汉式和懒汉式单例该如何选择?
答 :核心按资源类型和ISR需求选择:① 核心硬件(如核心总线、中断管理器)用饿汉式,兼顾ISR兼容性和线程安全;② 非核心硬件(如传感器、扩展模块)用懒汉式,节省嵌入式宝贵内存;③ 若系统内存极度受限(<128MB),优先懒汉式;④ 若需在中断中调用,必须用饿汉式。 - 问 :懒汉式单例的双重检查锁在嵌入式C++中需要注意什么?
答 :① 必须使用C++11及以上标准,保证内存模型的原子性,避免多线程下的半初始化问题;② 用std::mutex实现锁,避免嵌入式裸锁的死锁风险;③ 禁止在ISR中调用,锁会导致中断阻塞、系统死锁;④ 必须实现release()方法,手动释放内存和硬件资源。 - 问 :嵌入式单例为什么必须实现
release()方法?
答:因为嵌入式Linux无垃圾回收(GC),单例的资源(文件句柄、寄存器、内存)无法自动释放,若不手动释放,会导致资源泄漏,长期运行会让系统崩溃。
工厂模式专项
- 问 :嵌入式系统中,简单工厂模式违反开闭原则,为什么还推荐使用?
答 :① 嵌入式多为"小而美"系统,外设类型固定,无需频繁扩展,开闭原则的优先级低于简单、低内存;② 简单工厂类数量少,节省嵌入式宝贵的静态内存;③ 实现简单,开发和维护成本低,适合快速迭代;④ 后续若需扩展,可平滑迁移到工厂方法模式,接口完全兼容。 - 问 :如何在嵌入式Linux中实现工厂方法模式的动态扩展?
答 :① 定义抽象工厂和抽象产品接口,保证接口归一化;② 每个外设对应一个具体工厂和产品类;③ 将外设类型写入配置文件(如/etc/periph.conf),程序启动时动态读取;④ 用std::map建立"类型字符串→工厂类"的映射,动态创建工厂实例;⑤ 用智能指针管理实例,避免内存泄漏。 - 问 :简单工厂和工厂方法的平滑迁移方案是什么?
答 :先通过简单工厂实现核心功能,将产品的创建逻辑封装在工厂的create方法中;当需要扩展时,基于原有产品接口创建抽象工厂接口,将简单工厂的create方法拆分为多个具体工厂的create方法,业务层仅需将工厂的调用方式从"直接调用静态方法"改为"通过抽象工厂调用",原有产品代码无需修改。
通用设计模式专项
- 问 :资源受限的嵌入式系统,是否适合大量使用设计模式?
答 :不适合滥用,核心原则是够用即可、轻量级实现 :① 优先选择轻量级模式(如简单工厂>工厂方法,对象适配器>类适配器);② 避免使用复杂模式(如装饰器、迭代器、抽象工厂);③ 单个模式的类数量控制在合理范围,避免内存占用过高;④ 模式仅为工具,最终目标是代码可维护性与资源占用的平衡。 - 问 :嵌入式设计模式与桌面/服务器端设计模式的核心区别是什么?
答 :核心区别是嵌入式需兼顾硬件特性和资源限制:① 嵌入式关注ISR兼容性、线程安全(无锁/轻锁),桌面端无需考虑;② 嵌入式优先内存占用,选择轻量级实现,桌面端可接受类数量增多;③ 嵌入式模式需贴合硬件(如单例对应硬件唯一,状态模式对应设备状态机),桌面端更关注业务逻辑;④ 嵌入式模式需手动释放资源,桌面端有GC自动回收。 - 问 :嵌入式开发中,设计模式的核心落地原则是什么?
答 :三大核心原则:① 硬件优先 :所有模式的实现都需贴合嵌入式硬件特性(如唯一、中断、低功耗);② 资源可控 :优先轻量级实现,避免内存/算力的过度消耗;③ 最小修改:通过模式解耦硬件与业务,让底层硬件的修改对业务层的影响最小。
7. 总结
- 核心模式:单例(饿汉+懒汉)、工厂(简单+工厂方法)、适配器、观察者、状态模式;
- 按场景选择模式:根据嵌入式系统的规模、外设扩展需求、硬件类型选择合适的模式;
- 兼顾硬件与资源:所有模式的实现都需考虑ISR兼容性、线程安全、内存占用,避免脱离嵌入式实际;
- 拒绝过度设计:模式是为了解决问题,而非为了使用模式,小规模系统用简单代码即可解决的问题,无需强行使用设计模式。
核心宗旨 :嵌入式设计模式的核心是让硬件迭代更轻松、代码维护更简单、系统运行更稳定 ,在资源受限的嵌入式场景下,找到设计优雅与性能高效的平衡点,才是最终的目标。