首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

設計模式在 C 語言中的實踐:適配器模式 (對象適配器) 完整實現與場景解析

     編輯:關於C語言

前言

適配器模式將**不兼容的接口**轉換為**客戶期望的接口**,使原本無法協作的類能夠協同工作。雖然 C 語言無類/繼承,但通過**結構體 + 函數指針**可完美實現對象適配器模式。本文給出標準 C 實現、C# 對比參考、適用場景判斷、重構優先原則。

一、模式結構 (對象適配器)

+----------------+          +----------------+          +----------------+
|    Client      |  --->    |    Target      |  --->    |    Adaptee     |
| (期望 Target)  |  介面    | (抽象目標介面)  |  委託    | (被適配者/舊系統) |
+----------------+          +----------------+          +----------------+
                                   ▲
                                   │ 繼承/實現
                                   │
                            +----------------+
                            |    Adapter     |
                            | (持有 Adaptee) |
                            +----------------+

二、標準 C 實現 (函數指針模擬多態)

2.1 目標介面


// target.h
typedef struct Target Target;

struct Target {
    void (*request)(Target *self); // 虛函數指針
};

// 默認實現
void target_default_request(Target *self) {
    printf("普通請求!\n");
}

Target *target_create() {
    Target *t = malloc(sizeof(Target));
    t->request = target_default_request;
    return t;
}

2.2 被適配者 (舊系統/第三方庫)


// adaptee.h
typedef struct Adaptee Adaptee;

struct Adaptee {
    // 無 request(),只有特殊介面
};

void adaptee_specific_request(Adaptee *self) {
    printf("特殊請求!\n");
}

Adaptee *adaptee_create() {
    return calloc(1, sizeof(Adaptee));
}

2.3 適配器 (核心:組合 + 委託)


// adapter.h
#include "target.h"
#include "adaptee.h"

typedef struct Adapter {
    Target target;       // 嵌入 Target (繼承模擬:前綴相同即可向上轉型)
    Adaptee *adaptee;    // 持有被適配者 (組合)
} Adapter;

// 適配器的 request 實現:委託給 adaptee
void adapter_request(Target *self) {
    Adapter *adapter = (Adapter *)self; // 向下轉型
    adaptee_specific_request(adapter->adaptee);
}

Target *adapter_create() {
    Adapter *a = malloc(sizeof(Adapter));
    a->target.request = adapter_request; // 覆寫虛函數
    a->adaptee = adaptee_create();
    return (Target *)a; // 向上轉型返回 Target*
}

void adapter_destroy(Target *self) {
    Adapter *a = (Adapter *)self;
    free(a->adaptee);
    free(a);
}

2.4 客戶端代碼


int main() {
    Target *t = adapter_create(); // 客戶端只認 Target*
    t->request(t);                // 輸出: 特殊請求!
    adapter_destroy(t);
    return 0;
}

三、C# 參考實現 (原文語言)


// Target
class Target {
    public virtual void Request() => Console.WriteLine("普通請求!");
}

// Adaptee
class Adaptee {
    public void SpecificRequest() => Console.WriteLine("特殊請求!");
}

// Adapter (對象適配器)
class Adapter : Target {
    private Adaptee _adaptee = new Adaptee();
    public override void Request() => _adaptee.SpecificRequest();
}

// Client
Target target = new Adapter();
target.Request(); // 特殊請求!

四、適用場景判斷清單

場景特徵 是否適用適配器 替代方案優先級
第三方庫/舊代碼接口與當前系統不兼容,且**無法修改源碼** ✅ **首選** 無
需要復用現有類,但接口不匹配新系統需求 ✅ 適用 重構統一接口 (若可控)
設計階段預見接口不一致 ❌ **不適用** **統一接口設計** (介面隔離、依賴倒置)
可修改源碼,且接口差異小 ❌ 過度設計 **直接重構/修改接口**
需要適配多個不同來源的類到同一目標接口 ✅ 適用 統一門面模式

五、核心優缺點

  • ✅ **解耦**:客戶端依賴抽象 Target,不依賴具體 Adaptee。
  • ✅ **復用**:無需修改 Adaptee 即可納入新體系。
  • ✅ **單一職責**:適配邏輯集中在 Adapter,不污染業務代碼。
  • ⚠️ **增加複雜度**:引入中間層,調試需跳轉。
  • ⚠️ **性能微損**:多一次函數指針調用/虛函數分發 (通常可忽略)。
  • ⚠️ **僅適配接口**:若 Adaptee 內部狀態/行為語義與 Target 根本衝突,適配器無能為力。

六、C 語言特有實現技巧

  1. 結構體嵌入模擬繼承:struct Adapter { Target target; ...; },Target* 與 Adapter* 內存佈局兼容 (首地址相同),直接強轉實現向上/向下轉型。
  2. 函數指針表 (vtable) 實現多態:target.request = adapter_request; 動態綁定。
  3. 生命周期管理:create/destroy 成對函數,構造時初始化 vtable、創建被適配者,析構時逆序釋放。
  4. 不透明指針:頭文件僅前向聲明 struct Target;,隱藏實現細節,降低編譯依賴。

七、總結:適配器是「最後手段」,而非「首選方案」

設計階段統一接口 > 重構修正接口 > 適配器彌補接口

只有面對**不可修改的第三方庫、歷史遺留系統、硬件驅動接口**等「硬約束」時,才應引入適配器。在可控代碼中,**介面隔離原則 (ISP) + 依賴倒置原則 (DIP) + 統一抽象層** 才是從根源消除接口不兼容的正道。C 語言通過「結構體嵌入 + 函數指針 + 組合」零開銷實現對象適配器,是系統級開發中接口治理的利器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved