一.背景介紹
在我們的印象中,設計模式是由面向對象的語言(C++、JAVA)才能完成的,而 C 語言是面向過程的語言,不能實現設計模式。但C 語言中有 函數指針 、回調函數 等機制,使用這些機制便能寫出面向對象的優秀程序。
LINUX 操作系統,采用 C 語言寫的,但是里面很多模塊實現都是通過面向對象的設計方式實現的,這也是很多人初看 Linux 源碼看得云里霧里的原因。
面向過程(Procedure Oriented 簡稱 PO): 把事情拆分成幾個步驟(相當于拆分成一個個的方法和數據),然后按照一定的順序執行。
面向對象(Object Oriented 簡稱 OO): 面向對象會把事物抽象成對象的概念,先抽象出對象,然后給對象賦一些屬性和方法,然后讓每個對象去執行自己的方法。
二.設計實現
在 C++中實現設計模式主要是通過virtual 關鍵字修飾的虛函數來實現的,在 C 語言中沒有這個操作,但是我們可以通過指針函數加結構體進行實現。我們先簡單了解一下指針函數:
typedef void (*pfunc)(int); //此處定義了pfunc這個函數指針
//定義一個test_func函數,與pfunc的返回類型和參數是一致的,只有名字不同
//若不一致則不能定義pfunc的指針指向test_func
void test_func(int id)
{
printf("id=%d \\n", id);
}
//使用func指向test_func()
pfunc func = test_func;
//調用func(id) 與 test_func(id)實現的功能一致
func(1);//即可使用
//與結構體結合
typedef struct func_t
{
void (*func)(int id);
}func_t;
func_t f;
f.func = test_func; //結構體的函數指針指向test_func()
f.func(1); //調用結構體的函數指針,與調用test_func(1)效果一致
//通過上述例子,我們可以看到通過結構體+函數指針可以實現封裝信息并指向另外一個函數
//有了這個特性我們可以實現一個簡單工廠模式
本設計實現一個簡單的工廠模式,一個生產不同種類水果的工廠。每種水果有兩個屬性show() eat(),實現 apple、banana、pear 這 3 種水果。
//設計抽象定義接口
//定義的抽象的水果接口
typedef struct Ifruit_t
{
void (*show)(void* obj); //顯示信息
void (*eat)(void* obj); //怎么eat
void *obj; //指向當前的結構體
}Ifruit_t;
下面程序是香蕉水果相關的定義, 另外的 蘋果與梨 定義與這個幾乎完全一致,此處考慮篇幅問題,不全部貼出來了。
//------------------------------------------------
//實現香蕉相關的定義
//------------------------------------------------
typedef struct banana_t //與Ifruit_t的定義一致
{
void (*show)(void* obj); //顯示信息
void (*eat)(void* obj); //怎么eat
void *obj; //指向當前的結構體
}banana_t;
static void banana_show(void* obj) //使用static修飾,避免被外部直接調用
{
printf("我是香蕉!\\n");
}
static void banana_eat(void* obj) //使用static修飾,避免被外部直接調用
{
printf("操作: 先剝掉皮,再吃!\\n");
}
//香蕉的構造函數
banana_t* constructor_banana(void) //不使用static修飾,讓外部直接調用
{
banana_t* obj = (banana_t*)malloc(sizeof(banana_t));
obj- >show = banana_show; //給指針函數賦值,后面才能被調用
obj- >eat = banana_eat; //給指針函數賦值,后面才能被調用
obj- >obj = obj; //obj指向當前結構體指針
return obj;
}
工廠實現的函數 如下:
enum FruitType //枚舉類型
{
APPLE, //蘋果
BANANA, //香蕉
PEAR, //梨
};
Ifruit_t* factor_create_fruit(enum FruitType type) //工廠:生成水果的
{
Ifruit_t *fruit=NULL;
switch (type)
{
case APPLE:
fruit = (Ifruit_t *)constructor_apple();
printf("工廠: 生產蘋果!\\n");
break;
case BANANA:
fruit = (Ifruit_t *)constructor_banana();
printf("工廠: 生產香蕉!\\n");
break;
case PEAR:
fruit = (Ifruit_t *)constructor_pear();
printf("工廠: 生產梨!\\n");
break;
default:
break;
}
return fruit;
}
main 使用流程 如下。工廠設計模式它的優勢是易于擴展,此處我們實現了香蕉、蘋果、梨種水果,當業務中需要西瓜時,我們寫一個西瓜相關的結構體并實現對應函數,實現方式和上面 banana 實現方式一致,而對于西瓜的使用依舊是如下調用fruit->show(NULL); fruit->eat(NULL);,這樣我們主體的業務邏輯便能完成以較小的改動來添加一個新的模塊功能。
int main(void)
{
Ifruit_t *fruit=NULL;
fruit = factor_create_fruit(APPLE); //生成蘋果
//每一次有新的水果添加進來,步驟都和下面一樣的,易于擴展
fruit- >show(NULL); //顯示蘋果
fruit- >eat(NULL); //操作蘋果
free(fruit); //不使用了,釋放資源
printf("\\n");
fruit = factor_create_fruit(BANANA);
fruit- >show(NULL);
fruit- >eat(NULL);
free(fruit);
printf("\\n");
fruit = factor_create_fruit(PEAR);
fruit- >show(NULL);
fruit- >eat(NULL);
free(fruit);
return 0;
}
三.運行測試
運行結果**:
四.總結
雖然C語言是面向過程的編程語言,但是我們在設計程序的時候,可以考慮用面向對象的方式去設計,這樣提高我們程序的“高內聚、低耦合”特性,便于維護。
評論
查看更多