在嵌入式开发和底层系统编程中,接口不兼容问题就像不同国家的电源插座标准差异一样令人头疼。想象一下,你带着美国标准的电器来到欧洲,没有适配器就完全无法使用。同样地,在C语言项目中,我们常常遇到以下几种典型场景:
这些问题如果采用重写代码的方式解决,不仅工作量大,而且可能引入新的bug。适配器模式正是为解决这类接口兼容问题而生的利器。
在C语言中实现适配器模式,我们需要特别注意以下几点特性:
提示:在嵌入式开发中,对象适配器通常是更好的选择,因为它通过指针组合而非结构体嵌套来实现,内存开销更小,灵活性更高。
适配器模式包含三个关键角色,它们在C语言中的对应实现如下:
目标接口(Target):
c复制typedef struct {
int (*init)(void);
int (*read)(uint8_t*, uint16_t);
void (*close)(void);
} SensorInterface;
适配者(Adaptee):
c复制// 旧传感器驱动
uint8_t legacy_sensor_start(uint32_t addr);
void legacy_get_data(uint8_t* buf);
适配器(Adapter):
适配器的核心工作是进行接口转换,主要包括以下几种类型:
参数转换:
返回值转换:
调用顺序调整:
错误处理转换:
类适配器通过结构体嵌套来模拟继承关系,下面是完整实现示例:
c复制// 目标接口
typedef struct {
int (*init)(void);
int (*read)(uint8_t*, uint16_t);
} TargetInterface;
// 适配者
typedef struct {
void (*power_on)(uint8_t level);
uint8_t (*get_reading)(void);
} Adaptee;
// 类适配器
typedef struct {
TargetInterface target; // 嵌套目标接口
Adaptee adaptee; // 包含适配者
uint8_t power_level;
} ClassAdapter;
// 适配器初始化实现
static int adapter_init(void) {
ClassAdapter* self = (ClassAdapter*)&g_adapter;
self->adaptee.power_on(self->power_level);
return 0; // 统一返回0表示成功
}
// 适配器读取实现
static int adapter_read(uint8_t* buf, uint16_t len) {
if (buf && len > 0) {
ClassAdapter* self = (ClassAdapter*)&g_adapter;
*buf = self->adaptee.get_reading();
return 1;
}
return -1;
}
// 全局实例
static ClassAdapter g_adapter = {
.target = {
.init = adapter_init,
.read = adapter_read
},
.power_level = 3
};
// 获取适配器接口
TargetInterface* get_class_adapter(void) {
return &g_adapter.target;
}
类适配器的特点:
对象适配器通过组合方式实现,更灵活且内存高效:
c复制// 对象适配器
typedef struct {
TargetInterface target; // 实现目标接口
Adaptee* adaptee; // 指向适配者的指针
uint8_t power_level;
} ObjectAdapter;
// 创建适配器实例
ObjectAdapter* create_adapter(Adaptee* adaptee, uint8_t power) {
ObjectAdapter* adapter = malloc(sizeof(ObjectAdapter));
if (adapter) {
adapter->target.init = obj_adapter_init;
adapter->target.read = obj_adapter_read;
adapter->adaptee = adaptee;
adapter->power_level = power;
}
return adapter;
}
// 初始化实现
static int obj_adapter_init(void) {
ObjectAdapter* self = get_current_adapter();
if (self && self->adaptee) {
self->adaptee->power_on(self->power_level);
return 0;
}
return -1;
}
// 读取实现
static int obj_adapter_read(uint8_t* buf, uint16_t len) {
ObjectAdapter* self = get_current_adapter();
if (self && self->adaptee && buf && len > 0) {
*buf = self->adaptee->get_reading();
return 1;
}
return -1;
}
对象适配器的优势:
| 特性 | 类适配器 | 对象适配器 |
|---|---|---|
| 内存占用 | 较大(包含完整适配者) | 较小(仅指针) |
| 灵活性 | 适配者固定 | 可动态更换适配者 |
| 扩展性 | 需修改适配器结构 | 新增适配者更方便 |
| 适用场景 | 简单固定接口 | 复杂多变接口 |
| 代码复杂度 | 较低 | 略高(需管理指针) |
实际工程中选择建议:在资源紧张的嵌入式系统中,优先考虑对象适配器;对于简单稳定的接口转换,可以使用类适配器简化实现。
假设我们需要统一多种温度传感器的接口:
c复制// 统一的目标接口
typedef struct {
float (*get_temperature)(void);
} TempSensor;
// 适配者1:DS18B20驱动
float ds18b20_read_temp(void) {
// 实际读取代码
return 25.5f;
}
// 适配者2:DHT11驱动
int dht11_read(uint8_t* temp, uint8_t* humidity) {
// 实际读取代码
*temp = 26;
return 0;
}
// DS18B20适配器
typedef struct {
TempSensor sensor;
} DS18B20Adapter;
static float ds18b20_adapter_read(void) {
return ds18b20_read_temp();
}
TempSensor* create_ds18b20_adapter(void) {
DS18B20Adapter* adapter = malloc(sizeof(DS18B20Adapter));
if (adapter) {
adapter->sensor.get_temperature = ds18b20_adapter_read;
}
return &adapter->sensor;
}
// DHT11适配器
typedef struct {
TempSensor sensor;
} DHT11Adapter;
static float dht11_adapter_read(void) {
uint8_t temp = 0;
dht11_read(&temp, NULL);
return (float)temp;
}
TempSensor* create_dht11_adapter(void) {
DHT11Adapter* adapter = malloc(sizeof(DHT11Adapter));
if (adapter) {
adapter->sensor.get_temperature = dht11_adapter_read;
}
return &adapter->sensor;
}
使用示例:
c复制TempSensor* sensor1 = create_ds18b20_adapter();
TempSensor* sensor2 = create_dht11_adapter();
printf("Temp1: %.1f\n", sensor1->get_temperature());
printf("Temp2: %.1f\n", sensor2->get_temperature());
统一项目中的日志接口:
c复制// 目标日志接口
typedef struct {
void (*info)(const char*);
void (*error)(const char*);
} Logger;
// 第三方日志库
void third_party_log(int level, const char* msg);
// 日志适配器
typedef struct {
Logger logger;
} LogAdapter;
static void log_info(const char* msg) {
third_party_log(1, msg);
}
static void log_error(const char* msg) {
third_party_log(2, msg);
}
Logger* create_logger(void) {
LogAdapter* adapter = malloc(sizeof(LogAdapter));
if (adapter) {
adapter->logger.info = log_info;
adapter->logger.error = log_error;
}
return &adapter->logger;
}
对象适配器需要动态分配内存,容易造成泄漏:
解决方案:
示例:
c复制void destroy_logger(Logger* logger) {
if (logger) {
LogAdapter* adapter = container_of(logger, LogAdapter, logger);
free(adapter);
}
}
适配器可能在多任务环境下被调用:
解决方案:
示例:
c复制typedef struct {
Logger logger;
osMutexId_t mutex;
} SafeLogAdapter;
static void safe_log_info(const char* msg) {
SafeLogAdapter* adapter = get_adapter();
osMutexAcquire(adapter->mutex, osWaitForever);
third_party_log(1, msg);
osMutexRelease(adapter->mutex);
}
减少转换开销:
内存优化:
代码组织:
当需要多层接口转换时,可以串联多个适配器:
c复制// 中间接口
typedef struct {
int (*get_value)(void);
} MiddleInterface;
// 链式适配器实现
typedef struct {
TempSensor sensor;
MiddleInterface* middle;
} ChainAdapter;
static float chain_adapter_read(void) {
ChainAdapter* self = get_current_adapter();
return (float)self->middle->get_value();
}
TempSensor* create_chain_adapter(MiddleInterface* middle) {
ChainAdapter* adapter = malloc(sizeof(ChainAdapter));
if (adapter) {
adapter->sensor.get_temperature = chain_adapter_read;
adapter->middle = middle;
}
return &adapter->sensor;
}
实现接口的双向转换:
c复制typedef struct {
InterfaceA a;
InterfaceB b;
} TwoWayAdapter;
// A到B的转换
static void a_to_b_operation(void) {
TwoWayAdapter* self = get_current_adapter();
// 调用B接口实现A接口
}
// B到A的转换
static void b_to_a_operation(void) {
TwoWayAdapter* self = get_current_adapter();
// 调用A接口实现B接口
}
推迟资源密集型操作:
c复制typedef struct {
SensorInterface sensor;
Adaptee* adaptee;
uint8_t initialized;
} LazyAdapter;
static int lazy_init(void) {
LazyAdapter* self = get_current_adapter();
if (!self->initialized) {
if (init_adaptee(self->adaptee) == 0) {
self->initialized = 1;
}
}
return self->initialized ? 0 : -1;
}
在实际嵌入式项目中,适配器模式的价值不仅在于解决接口兼容问题,更重要的是它提供了一种清晰的架构思路,让不同层级的代码能够通过定义良好的接口进行协作,大大提高了系统的可维护性和可扩展性。