文章目錄
- 1. 設計模式核心概念與C語言實現基礎
- 2. 常用設計模式詳解
- 模式一:策略模式(Strategy Pattern)
- 模式二:觀察者模式(Observer Pattern)
- 模式三:單例模式(Singleton Pattern)
- 模式四:工廠方法模式(Factory Method Pattern)
1. 設計模式核心概念與C語言實現基礎
設計模式是一套被反復使用、多數人知曉、經過分類編目的、代碼設計經驗的總結。它描述了在軟件設計過程中一些不斷重復發生的問題,以及該問題的解決方案的核心。
在C語言中實現設計模式,主要依賴于以下技術來模擬面向對象特性:
- 結構體(Structs): 用于封裝數據,模擬類的屬性。
- 函數指針(Function Pointers): 用于封裝行為,模擬類的方法。這是實現多態(Polymorphism) 和繼承(Inheritance) 的關鍵。
- 頭文件(.h)和源文件(.c): 用于實現封裝(Encapsulation) 和信息隱藏。頭文件暴露結構體和公共函數接口,源文件隱藏私有數據和實現細節。
void指針(void*)
: 用于實現泛型編程,處理未知類型的數據。
2. 常用設計模式詳解
以下選擇四個在系統級、嵌入式、中間件等C語言主導領域非常實用的模式。
模式一:策略模式(Strategy Pattern)
1、意圖
定義一系列算法,將每個算法封裝起來,并使它們可以互相替換。策略模式讓算法的變化獨立于使用算法的客戶。
2、UML圖示
Context
:持有一個策略對象的引用,用接口與策略對象交互。Strategy
:策略接口,聲明了所有具體策略的通用方法(execute
)。ConcreteStrategy
:實現了策略接口的具體算法。
3、C代碼實現
//--- strategy.h ---
// 聲明策略接口(用結構體模擬)
typedef struct Strategy Strategy;
struct Strategy {void (*execute)(void); // 函數指針,代表算法接口
};// 上下文類,用于連接策略
typedef struct Context Context;
struct Context {Strategy *strategy;void (*setStrategy)(Context *, Strategy *);void (*executeStrategy)(Context *);
};// 上下文的構造函數
Context *context_new();//--- strategy.c ---
#include <stdio.h>
#include <stdlib.h>
#include "strategy.h"// 上下文的方法實現
static void setStrategy(Context *self, Strategy *strategy) {self->strategy = strategy;
}static void executeStrategy(Context *self) {if (self->strategy && self->strategy->execute) {self->strategy->execute(); // 委托給具體策略} else {printf("No strategy set.\n");}
}// 上下文構造函數
Context *context_new() {Context *ctx = (Context *)malloc(sizeof(Context));ctx->strategy = NULL;ctx->setStrategy = setStrategy;ctx->executeStrategy = executeStrategy;return ctx;
}//--- concrete_strategy_a.c ---
#include <stdio.h>
#include "strategy.h"// 具體策略A的實現
static void execute_a(void) {printf("Executing strategy A: Quick sort algorithm.\n");
}// 具體策略A的“構造函數”,創建一個策略對象
Strategy *strategy_a_new() {Strategy *s = (Strategy *)malloc(sizeof(Strategy));s->execute = execute_a; // 將函數指針指向具體實現return s;
}//--- concrete_strategy_b.c ---
// ... 類似地實現策略B
static void execute_b(void) {printf("Executing strategy B: Merge sort algorithm.\n");
}
Strategy *strategy_b_new() {Strategy *s = (Strategy *)malloc(sizeof(Strategy));s->execute = execute_b;return s;
}//--- main.c ---
int main() {Context *ctx = context_new();Strategy *strategyA = strategy_a_new();Strategy *strategyB = strategy_b_new();// 使用策略Actx->setStrategy(ctx, strategyA);ctx->executeStrategy(ctx); // 輸出: Executing strategy A...// 動態切換為策略Bctx->setStrategy(ctx, strategyB);ctx->executeStrategy(ctx); // 輸出: Executing strategy B...// 釋放內存...return 0;
}
4、技術與內容
- 技術: 使用結構體嵌套函數指針來模擬接口和多態。
Context
依賴于抽象的Strategy
接口,而非具體實現。 - 內容: 遵循開閉原則(對擴展開放,對修改關閉)。添加新算法(新策略)只需創建新的
ConcreteStrategy
,而無需修改Context
的代碼。常用于算法選擇、文件格式轉換、日志策略等場景。
模式二:觀察者模式(Observer Pattern)
1、意圖
定義對象間的一種一對多的依賴關系,當一個對象(主題)的狀態發生改變時,所有依賴于它的對象(觀察者)都得到通知并被自動更新。
2、UML圖示
3、C代碼實現(簡化版)
//--- observer.h ---
typedef struct Observer Observer;
typedef struct Subject Subject;// 觀察者接口
struct Observer {void (*update)(Observer *self, int state); // 通知函數
};// 主題基類
struct Subject {Observer *observers[10]; // 簡單的觀察者數組(實際應用可用鏈表)int count;void (*attach)(Subject *, Observer *);void (*detach)(Subject *, Observer *);void (*notify)(Subject *);
};//--- subject.c ---
#include "observer.h"
#include <stdio.h>static void attach(Subject *self, Observer *obs) {if (self->count < 10) {self->observers[self->count++] = obs;}
}
static void detach(Subject *self, Observer *obs) { /* ... 從數組中移除 ... */ }
static void notify(Subject *self) {for (int i = 0; i < self->count; i++) {if (self->observers[i] && self->observers[i]->update) {// 這里需要知道狀態,通常ConcreteSubject會重寫notify// 為了簡化,我們假設傳遞一個虛擬狀態值0self->observers[i]->update(self->observers[i], 0);}}
}// 主題的“基類”構造函數
Subject *subject_new() {Subject *sub = (Subject *)malloc(sizeof(Subject));sub->count = 0;sub->attach = attach;sub->detach = detach;sub->notify = notify;return sub;
}//--- concrete_subject.c ---
// 具體主題,擁有狀態
typedef struct {Subject base; // 模擬“繼承”,Base放在第一個成員,可以實現強制轉換int state;
} ConcreteSubject;ConcreteSubject *concrete_subject_new() {ConcreteSubject *cs = (ConcreteSubject *)malloc(sizeof(ConcreteSubject));cs->base = *subject_new(); // 初始化基類部分cs->state = 0;return cs;
}
// 重寫notify?或者提供setState方法,在setState中調用notify
void concrete_subject_set_state(ConcreteSubject *self, int state) {self->state = state;self->base.notify((Subject *)self); // 通知所有觀察者
}//--- concrete_observer.c ---
typedef struct {Observer base;ConcreteSubject *subject; // 觀察者需要知道它所觀察的主題
} ConcreteObserver;static void update(Observer *self, int state) {ConcreteObserver *co = (ConcreteObserver *)self; // 獲取包含自己的大結構體// 從主題獲取真實狀態int actual_state = co->subject->state;printf("Observer %p: Subject's state changed to %d\n", (void*)self, actual_state);
}ConcreteObserver *concrete_observer_new(ConcreteSubject *sub) {ConcreteObserver *co = (ConcreteObserver *)malloc(sizeof(ConcreteObserver));co->base.update = update; // 實現接口co->subject = sub;sub->base.attach((Subject *)sub, (Observer *)co); // 注冊自己return co;
}//--- main.c ---
int main() {ConcreteSubject *subject = concrete_subject_new();ConcreteObserver *obs1 = concrete_observer_new(subject);ConcreteObserver *obs2 = concrete_observer_new(subject);// 改變主題狀態,觀察者會自動被通知concrete_subject_set_state(subject, 10);concrete_subject_set_state(subject, 20);return 0;
}
4、技術與內容
- 技術: 使用組合和函數指針。主題維護一個觀察者列表。關鍵技巧是結構體嵌套(
ConcreteSubject
包含Subject
,ConcreteObserver
包含Observer
)來實現一種形式的繼承和向上轉換。 - 內容: 實現了發布-訂閱機制,徹底解耦了主題和觀察者。主題不知道觀察者的具體類,只知道它們實現了
Observer
接口。廣泛應用于GUI事件處理、數據監控、消息隊列等。
模式三:單例模式(Singleton Pattern)
1、意圖
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
2、UML圖示
- 私有構造函數防止外部
new
。 - 靜態變量
instance
持有唯一實例。 - 靜態方法
getInstance()
控制實例的創建和訪問。
3、C代碼實現
//--- singleton.h ---
typedef struct Singleton Singleton;// 獲取單例實例的全局函數
Singleton *singleton_get_instance(void);// 某個業務方法
void singleton_some_business_operation(Singleton *self);//--- singleton.c ---
#include <stdio.h>
#include <stdlib.h>// 定義單例結構體(可以包含各種數據)
struct Singleton {int some_value;// ... other data
};// 靜態局部變量,在第一次函數調用時初始化,并持續存在
static Singleton *instance = NULL;Singleton *singleton_get_instance(void) {if (instance == NULL) {// 首次調用,創建實例instance = (Singleton *)malloc(sizeof(Singleton));instance->some_value = 42; // 初始化數據printf("Singleton instance created.\n");}return instance;
}void singleton_some_business_operation(Singleton *self) {printf("Singleton operation called. Value is %d\n", self->some_value);
}//--- main.c ---
int main() {// Singleton s; // 錯誤:結構體是不完整類型,無法在外部創建// Singleton *s = malloc(sizeof(Singleton)); // 可以但不合規,破壞了模式// 正確獲取實例的方式Singleton *s1 = singleton_get_instance();Singleton *s2 = singleton_get_instance();if (s1 == s2) {printf("Both pointers point to the same instance.\n");}singleton_some_business_operation(s1);return 0;
}
4、技術與內容
- 技術: 使用靜態局部變量和靜態全局函數來控制實例的創建。通過不完整類型(在.h中只聲明struct Singleton,在.c中才定義它)來實現信息隱藏,防止外部直接創建實例。
- 內容: 確保一個類只有一個實例,并提供一個全局訪問點。常用于需要全局管理的資源,如日志管理器、數據庫連接池、應用程序配置等。注意多線程環境下的線程安全問題(上面的簡單實現不是線程安全的)。
模式四:工廠方法模式(Factory Method Pattern)
1、意圖
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
2、UML圖示
- Creator:聲明工廠方法,返回Product類型對象。
- ConcreteCreator:重寫工廠方法,返回一個具體的ConcreteProduct實例。
- Product:工廠方法創建的對象接口。
3、C代碼實現
//--- product.h ---
typedef struct Product Product;
struct Product {void (*operation)(Product *self);
};//--- creator.h ---
typedef struct Creator Creator;
struct Creator {// 工廠方法(函數指針),相當于一個“創建”接口Product *(*factoryMethod)(Creator *self);// 一個使用產品的操作void (*someOperation)(Creator *self);
};//--- concrete_creator_a.c ---
#include <stdio.h>
#include <stdlib.h>
#include "creator.h"
#include "product.h"// 具體產品A
typedef struct {Product base; // 模擬繼承,使ConcreteProductA* 可被當作Product*char specific_data[20];
} ConcreteProductA;static void product_a_operation(Product *self) {ConcreteProductA *pa = (ConcreteProductA *)self;printf("ConcreteProductA operation: %s\n", pa->specific_data);
}// 具體創建者A
typedef struct {Creator base;
} ConcreteCreatorA;// 工廠方法的具體實現:創建ConcreteProductA
static Product *factory_method_a(Creator *self) {ConcreteProductA *prod = (ConcreteProductA *)malloc(sizeof(ConcreteProductA));prod->base.operation = product_a_operation;snprintf(prod->specific_data, 20, "Made by A");return (Product *)prod;
}ConcreteCreatorA *concrete_creator_a_new() {ConcreteCreatorA *ca = (ConcreteCreatorA *)malloc(sizeof(ConcreteCreatorA));ca->base.factoryMethod = factory_method_a;return ca;
}//--- main.c ---
// 客戶代碼只依賴于Creator和Product接口
void client_code(Creator *creator) {printf("Client: I'm not aware of the creator's concrete class.\n");Product *product = creator->factoryMethod(creator);product->operation(product);free(product); // 假設需要釋放
}int main() {ConcreteCreatorA *creatorA = concrete_creator_a_new();client_code((Creator *)creatorA);// 未來可以輕松添加ConcreteCreatorB和ConcreteProductB// ConcreteCreatorB *creatorB = concrete_creator_b_new();// client_code((Creator *)creatorB);free(creatorA);return 0;
}
4、技術與內容
- 技術: 核心是函數指針,它將對象創建的邏輯抽象成了一個接口(factoryMethod)。結合結構體嵌套,讓具體的創建者決定創建何種具體的產品。
- 內容: 遵循依賴倒置原則(依賴抽象,而非具體實現)。客戶代碼(client_code)只與Creator和Product的抽象接口耦合,不與任何具體類耦合。這使得系統易于擴展,添加新的產品類型只需增加新的ConcreteCreator和ConcreteProduct,而無需修改現有客戶代碼。
模式名稱 | 主要技術手段(C語言) | 核心思想與內容 | 適用場景 |
---|---|---|---|
策略模式 | 結構體 + 函數指針(接口) | 分離算法,使其可獨立變化和替換 | 多種算法、策略可選的情況 |
觀察者模式 | 結構體嵌套 + 函數指針 + 鏈表/數組 | 一對多的依賴關系,發布-訂閱 | 事件驅動系統、數據監控 |
單例模式 | 靜態局部變量 + 不完整類型(信息隱藏) | 控制實例數目,提供全局訪問點 | 全局資源管理器 |
工廠方法模式 | 結構體嵌套 + 函數指針(工廠接口) | 將對象創建延遲到子類,解耦客戶代碼與具體類 | 框架設計,需要創建可擴展的對象家族 |
在C語言中應用設計模式,更多的是學習其思想精髓而非機械照搬面向對象的實現。通過靈活運用結構體、函數指針、模塊化編程等C語言核心特性,完全可以在過程式語言的范式中構建出靈活、可維護、可擴展的高質量代碼架構。