C语言adapter是什么?如何实现?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

什么是适配器模式?

核心思想:适配器模式是一种结构型设计模式,它允许不兼容的接口能够协同工作,就像现实世界中的电源适配器(插头转换器)能让你的美规电器在中规插座上使用一样,在软件中,适配器模式可以“包装”一个类的接口,使其看起来像是另一个接口,从而让原本无法交互的两个类能够连接起来。

c语言 adapter
(图片来源网络,侵删)

目的转换接口,而不是转换功能,它不会改变被包装对象的核心逻辑,只是提供一个“翻译层”。


适配器模式的应用场景

在 C 语言中,适配器模式通常用于以下几种情况:

  1. 封装第三方库:当你使用的第三方库(如某个网络库、数据库驱动)的接口不符合你项目现有的代码风格或架构时,你可以创建一个适配器,将第三方库的复杂接口封装成你项目中简单、统一的接口。
  2. 统一不同来源的数据结构:你的程序可能需要处理来自不同模块的数据,这些模块的数据结构可能略有不同(一个用 struct A,另一个用 struct B,但它们的核心字段相似),你可以为每个数据源创建一个适配器,将它们统一转换成你程序内部的标准数据结构。
  3. 保持向后兼容性:当你重构或升级一个旧模块的接口时,为了不影响其他依赖该模块的代码,可以先创建一个适配器,将新接口“伪装”成旧接口,待所有代码都更新完毕后,再逐步移除适配器。
  4. 模拟面向对象的多态:C 语言没有原生的类和继承,但可以通过函数指针和结构体来模拟,适配器模式可以巧妙地利用这一点,让不同的函数实现能够以统一的方式被调用。

适配器模式的两种主要实现方式

在 C 语言中,实现适配器模式主要有两种方式:类适配器对象适配器

对象适配器 - 最常用

这种方式通过组合来实现,适配器结构体内部包含一个指向被适配对象的指针,这是最灵活、最常用的方法,因为它只需要一个指向被适配对象的指针,而不需要知道其完整定义。

c语言 adapter
(图片来源网络,侵删)

示例:封装一个不同风格的排序函数

假设我们有一个旧的排序函数,它的签名很传统:

// 旧版排序函数 (被适配者)
// qsort 的标准签名
int compare_ints(const void* a, const void* b) {
    int arg1 = *(const int*)a;
    int arg2 = *(const int*)b;
    return (arg1 > arg2) - (arg1 < arg2); // 简单的比较
}
void old_sort(int* array, int size) {
    qsort(array, size, sizeof(int), compare_ints);
    printf("Using old qsort style.\n");
}

我们希望在项目中使用一个更现代、更“面向对象”风格的排序接口,比如这样:

// 期望的新接口 (目标接口)
// 一个包含函数指针的结构体,模拟一个“排序器”对象
typedef struct {
    void (*sort)(void* array, int size, int element_size, int (*compare)(const void*, const void*));
} ModernSorter;
void modern_sort_interface(void* array, int size, int element_size, int (*compare)(const void*, const void*)) {
    printf("Using modern sort interface.\n");
    // 在实际应用中,这里可能会调用更高效的算法
    qsort(array, size, element_size, compare);
}

这两个接口不兼容,我们创建一个对象适配器,让 old_sort 能够像 ModernSorter 一样被使用。

// 适配器实现
// 1. 定义适配器结构体,它持有一个被适配者的实例(这里我们不需要,因为 old_sort 是全局函数)
//    old_sort 是某个结构体的方法,这里就需要一个指向该结构体的指针。
// 2. 定义一个适配器函数,该函数实现了目标接口(ModernSorter),内部调用被适配者的方法。
// 适配器函数,它的签名匹配 ModernSorter.sort
void adapter_sort(void* array, int size, int element_size, int (*compare)(const void*, const void*)) {
    printf("Adapter: Translating modern call to old qsort.\n");
    // 直接调用被适配的函数
    old_sort((int*)array, size);
}
// 创建一个“现代排序器”实例,但实际上它使用我们的适配器
ModernSorter create_legacy_sort_adapter() {
    ModernSorter sorter;
    sorter.sort = adapter_sort; // 将适配器函数赋值给函数指针
    return sorter;
}

如何使用:

#include <stdio.h>
#include <stdlib.h>
// ... (上面的 old_sort, ModernSorter, adapter_sort, create_legacy_sort_adapter 代码) ...
int main() {
    int data[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(data) / sizeof(data[0]);
    // 使用适配器
    ModernSorter my_sorter = create_legacy_sort_adapter();
    printf("--- Before Sorting ---\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");
    // 调用方式完全符合“现代接口”
    my_sorter.sort(data, n, sizeof(int), compare_ints);
    printf("\n--- After Sorting ---\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");
    return 0;
}

输出:

--- Before Sorting ---
64 34 25 12 22 11 90 
Adapter: Translating modern call to old qsort.
Using old qsort style.
--- After Sorting ---
11 12 22 25 34 64 90 

在这个例子中,my_sorter 看起来像一个现代化的排序器,但它的 sort 方法实际上是由我们的 adapter_sort 函数实现的,而这个函数内部又调用了古老的 old_sortadapter_sort 就是那个“翻译官”。

类适配器 - 较少使用

这种方式通过多继承来实现,在 C++ 中,适配器类可以同时继承自目标接口和被适配者,但在 C 语言中,没有继承,所以实现起来非常笨拙且不推荐,它意味着将被适配者的结构体直接嵌入到适配器的结构体中

这会破坏封装性,因为适配者需要知道被适配者的完整内部结构,在 C 语言中,几乎总是优先选择对象适配器


优缺点总结

优点

  1. 提高复用性:可以让原本由于接口不兼容而无法一起工作的代码协同工作。
  2. 增加灵活性:可以随时替换被适配者,只要新的被适配者也能被同一个适配器“翻译”即可。
  3. 符合开闭原则:你可以通过引入新的适配器来扩展系统的功能,而无需修改现有代码。
  4. 封装性:客户端代码只与目标接口交互,完全不知道被适配者的存在和复杂性。

缺点

  1. 增加复杂性:会引入额外的间接层(适配器),使得系统调用链变长,可能增加理解和调试的难度。
  2. 性能开销:虽然通常很小,但额外的函数调用会带来微小的性能损耗。
  3. 过度使用:如果接口不兼容的问题可以通过重构代码来解决,那么使用适配器可能会掩盖设计上的缺陷。

在 C 语言中,适配器模式是一个强大的工具,主要用于接口转换,虽然它不像在 C++ 或 Java 中那样有语法糖支持,但通过结构体和函数指针的组合,可以非常灵活地实现。

核心要点

  • 目标:让不兼容的接口协同工作。
  • 常用实现对象适配器(组合),即用一个结构体包装被适配者的指针,并提供一个符合目标接口的函数。
  • 应用:封装第三方库、统一数据接口、保持向后兼容等。
  • 原则:优先使用组合而非“继承”(在C中表现为嵌入结构体),以保持良好的封装性。

当你发现两段代码“对不上话”时,不要急于修改其中一方,先考虑是否可以引入一个适配器来优雅地解决问题,这是一种非常专业和可维护的做法。

-- 展开阅读全文 --
头像
c语言 sound
« 上一篇 04-17
dede前台编辑文章,如何操作与权限设置?
下一篇 » 04-17

相关文章

取消
微信二维码
支付宝二维码

目录[+]