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