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