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

C語言實現適配器模式:以籃球員系統為例的完整代碼解析

     編輯:關於C語言

模式意圖與場景

適配器模式將一個類的介面轉換為客戶期望的另一個介面,使原本介面不兼容的類能夠協同工作。在 C 語言中,透過結構體嵌入與函數指針模擬「繼承」與「多型」,實現物件導向的適配器模式。

本例模擬籃球隊系統:

  • 目標介面:Player(攻擊/防守/銷毀)
  • 現有實作:Forwards、Center、Guards 均符合 Player 介面
  • 待適配類:ForeignCenter 介面不同(fattack/fdefend/fdestroy)
  • 適配器:ForeignCenterAdapter 封裝 ForeignCenter,對外暴露 Player 介面

核心代碼架構

1. 通用型別與巨集 typedef.h

#ifndef __TYPEDEF_H__
#define __TYPEDEF_H__

#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum _Ret { RET_OK, RET_FAIL } Ret;

/* 防禦性程式設計巨集 */
#define return_if_fail(p) \
    if (!(p)) { \
        printf("%s:%d Warning: " #p " Failed\n", __func__, __LINE__); \
        return; \
    }

#define return_val_if_fail(p, ret) \
    if (!(p)) { \
        printf("%s:%d Warning: " #p " Failed\n", __func__, __LINE__); \
        return (ret); \
    }

#define SAFE_FREE(p) do { if (p) { free(p); (p) = NULL; } } while (0)

#ifdef __cplusplus
}
#endif
#endif

2. 目標介面 player.h

#ifndef __PLAYER_H__
#define __PLAYER_H__

#include "typedef.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _Player Player;

struct _Player {
    char *name;
    void (*attack)(void *player);
    void (*defend)(void *player);
    void (*destroy)(void *player);
};

/* 內聯封裝函數,提供統一呼叫入口 */
static inline void player_attack(Player *thiz) {
    return_if_fail(thiz != NULL);
    if (thiz->attack) thiz->attack(thiz);
}
static inline void player_defend(Player *thiz) {
    return_if_fail(thiz != NULL);
    if (thiz->defend) thiz->defend(thiz);
}
static inline void player_destroy(Player *thiz) {
    return_if_fail(thiz != NULL);
    if (thiz->destroy) thiz->destroy(thiz);
}

#ifdef __cplusplus
}
#endif
#endif

3. 具體球員實作(以中鋒為例) centerplayer.h/.c

/* centerplayer.h */
#ifndef __CENTERPLAYER_H__
#define __CENTERPLAYER_H__
#include "player.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _Center Center;
struct _Center { Player player; };  // 組合:嵌入 Player 作為第一個成員

Center *CenterCreate(char *name);

#ifdef __cplusplus
}
#endif
#endif

/* centerplayer.c */
#include "centerplayer.h"

static void center_attack(void *thiz) {
    printf("中鋒 %s 進攻\n", ((Center *)thiz)->player.name);
}
static void center_defend(void *thiz) {
    printf("中鋒 %s 防守\n", ((Center *)thiz)->player.name);
}
static void center_destroy(void *thiz) {
    return_if_fail(thiz != NULL);
    SAFE_FREE(thiz);
}

Center *CenterCreate(char *name) {
    Center *thiz = malloc(sizeof(Center));
    if (thiz) {
        thiz->player.name = name;
        thiz->player.attack = center_attack;
        thiz->player.defend = center_defend;
        thiz->player.destroy = center_destroy;
    }
    return thiz;
}

4. 待適配類 foreigncenterplayer.h/.c

struct _ForeignCenter {
    char *name;
    void (*fattack)(void *player);
    void (*fdefend)(void *player);
    void (*fdestroy)(void *player);
};
/* 介面命名與 Player 不同,需適配 */

5. 適配器 foreigncenteradapter.c

#include "foreigncenterplayer.h"
#include "foreigncenteradapter.h"

static void foreign_center_adapter_attack(void *thiz) {
    foreign_center_attack(thiz);  // 委派給被適配物件
}
static void foreign_center_adapter_defend(void *thiz) {
    foreign_center_defend(thiz);
}
static void foreign_center_adapter_destroy(void *thiz) {
    foreign_center_destroy(thiz);
}

ForeignCenterAdapter *ForeignCenterAdapterCreate(char *name) {
    ForeignCenterAdapter *thiz = malloc(sizeof(ForeignCenterAdapter));
    if (thiz) {
        /* 關鍵:建立被適配物件,並將其指標作為 adapter 的 player 成員 */
        ForeignCenter *fc = ForeignCenterCreate(name);
        thiz->player.name = fc->name;
        thiz->player.attack = foreign_center_adapter_attack;
        thiz->player.defend = foreign_center_adapter_defend;
        thiz->player.destroy = foreign_center_adapter_destroy;
        /* 實際專案中需妥善管理 fc 的生命週期,此處簡化處理 */
    }
    return thiz;
}

6. 客戶端測試 test.c

#include "player.h"
#include "centerplayer.h"
#include "foreigncenteradapter.h"

int main(void) {
    Player *yao = (Player *)CenterCreate("姚明");
    Player *bateer = (Player *)ForeignCenterAdapterCreate("巴蒂爾");

    player_attack(yao);       // 輸出:中鋒 姚明 進攻
    player_defend(bateer);    // 輸出:外籍中鋒 巴蒂爾 防守
    player_attack(bateer);    // 輸出:外籍中鋒 巴蒂爾 進攻

    player_destroy(yao);
    player_destroy(bateer);
    return 0;
}

7. 建構腳本 Makefile

EXE := test
SRCS := test.c \
        player.h foreigncenteradapter.h foreigncenteradapter.c \
        foreigncenterplayer.h foreigncenterplayer.c \
        forwardsplayer.h forwardsplayer.c \
        centerplayer.h centerplayer.c \
        guardsplayer.h guardsplayer.c \
        typedef.h

all: $(EXE)

$(EXE): $(SRCS)
	gcc -g $^ -o $(EXE)

clean:
	rm -f $(EXE) *~

C 語言實現適配器模式的關鍵技巧總結

技巧說明
結構體嵌入模擬繼承將基底結構體(Player)作為衍生結構體(Center、ForeignCenterAdapter)的第一個成員,利用記憶體布局相容性實現向上轉型
函數指針實現多型在基底結構體中定義函數指針成員,各衍生類在建構函數中綁定具體實作
不透明指針封裝頭檔僅前向宣告 struct _Player;,隱藏實作細節,降低耦合
防禦性巨集return_if_fail、SAFE_FREE 統一錯誤處理與記憶體釋放風格
內聯封裝函數player_attack() 等內聯函數提供統一呼叫介面,隱藏 thiz->attack(thiz) 細節

編譯執行驗證

$ make
gcc -g test.c ... -o test
$ ./test
中鋒 姚明 進攻
外籍中鋒 巴蒂爾 防守
外籍中鋒 巴蒂爾 進攻

延伸思考

  • 雙向適配器:若需讓 ForeignCenter 也能當 Player 用,可在適配器中雙向綁定函數指針
  • 記憶體管理:生產級代碼應在 ForeignCenterAdapterCreate 內部持有 ForeignCenter*,並在 destroy 時一併釋放
  • C++ 對比:C++ 可直接使用 class Adapter : public Target, private Adaptee(類適配器)或組合(物件適配器),編譯器自動處理虛函數表與轉型
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved