這里分享一些關(guān)于C語言程序設(shè)計方面的心得吧逻澳,一點淺見,見笑見笑暖呕。
C語言也可以實現(xiàn)面向?qū)ο笤O(shè)計
面向?qū)ο笫且环N方法斜做,雖然C語言不是面向?qū)ο笳Z言也可以做到面向?qū)ο笤O(shè)計。首先來看看對象主要包含什么:
- 屬性集合
- 方法集合
- 方法里能訪問對象上下文
我們先看看屬性湾揽,C語言里的結(jié)構(gòu)體就能實現(xiàn)這一點瓤逼,一個結(jié)構(gòu)體定義一些字段,這些字段的集合是不是就能看做是屬性集合了库物。
typedef struct Person_st
{
char name[20];
int age;
}Person_t
然后再來看看2霸旗,我們認為按約定定義好一些方法,說這個就是對象的方法就行戚揭,面向?qū)ο蟛皇欠堑谜f同過"."或"->"調(diào)用才是對象方法吧诱告。比如:
PersionSetName();
PersionSetAge();
這里先不考慮函數(shù)里面實現(xiàn)的問題,我們可以約定這么定義對象的方法『對象名+方法名』民晒。當(dāng)然你想要的『->』調(diào)用也是可以的精居。
typedef struct Person_st
{
char name[20];
int age;
void (*setName)();
void (*setAge)();
}Person_t
Person persion;
person->setName();
person->setAge();
通過函數(shù)指針來實現(xiàn)锄禽,事實上Linux內(nèi)核中就是這么做的。
最后再來看3箱蟆,如何訪問對象上下文的問題沟绪,在C++或其他面向?qū)ο笳Z言中,直接在方法內(nèi)通過this來訪問對象是天經(jīng)地義的事空猜,但是在C語言由于本身不支持就需要我們費些功夫來實現(xiàn)了绽慈。
方式1
typedef struct Person_st
{
char name[20];
int age;
}Person_t
void PersonSetName(Person_t *p, const char *name);
void PersonSetAge(Person_t *p, int age);
方式2
typedef struct Person_st
{
char name[20];
int age;
void (*setName)(Person_t *p, const char *name);
void (*setAge)(Person_t *p, int age);
}Person_t
我們主動把對象傳進給函數(shù)。這里我們就能在C語言上進行面向?qū)ο蟮某绦蛟O(shè)計了辈毯。至于方式1和方式2哪個好用坝疼,我個人喜歡用方式1,一個是不需要初始化函數(shù)指針谆沃,再者就是結(jié)構(gòu)體占用的內(nèi)存更小钝凶。
模塊化設(shè)計
其實程序設(shè)計和任務(wù)分解一樣,你把一個大的功能不斷細分唁影,把一個大模塊分解成小模塊耕陷,最終無數(shù)個小模塊組成一個完整的程序。每次面對的都是一個個小功能据沈,一者是當(dāng)前需要面對的情況簡單了哟沫,一者就是需要思考的點少了,不會出現(xiàn)無從下手的情況锌介。有很多將程序設(shè)計方面的書這里就不多細說了嗜诀。
非阻塞系統(tǒng)設(shè)計
另外說一下自己關(guān)于程序設(shè)計的一點感悟吧,非阻塞系統(tǒng)設(shè)計孔祸。
這里先說接口隆敢,一般接口分為兩種,一種是阻塞的崔慧,等到所有數(shù)據(jù)處理完成再返回拂蝎,一種是非阻塞的,調(diào)用接口時只是相當(dāng)于一個觸發(fā)信號惶室,等處理完成一般通過回調(diào)函數(shù)返回匣屡。
這里分析一下兩種方式,第一種接口拇涤,設(shè)計簡單,使用也比較簡單誉结,都是線性思維的鹅士,但是帶來的影響就是整個系統(tǒng)會阻塞,不說單片機阻塞了就相當(dāng)于把整個系統(tǒng)阻塞了惩坑,就算是用多線程也會帶來線程同步的問題掉盅,而線程同步是最容易出問題也是出了問題最不好查的地方也拜。
第二種,所有的接口都是非阻塞的趾痘,需要等待獲取結(jié)果的通過回調(diào)函數(shù)來返回慢哈。不好的地方就是這時候思維不是線性的,先調(diào)用接口然后再在回調(diào)返回的地方再處理結(jié)果永票。好處就是你不會因為處理一項事物而阻塞了其他事物的執(zhí)行卵贱,系統(tǒng)的響應(yīng)實時性是高的。
特別是在多線程系統(tǒng)中侣集,假設(shè)所有的邏輯都是非阻塞的也就沒必要再開線程了键俱,也就根本不需要考慮線程間同步的問題。當(dāng)然這里是需要一點小技巧的世分,因為系統(tǒng)里總有耗時的任務(wù)编振,原則上是只把耗時的任務(wù)交給線程處理,在處理完成有結(jié)果的時候把處理結(jié)果回調(diào)時又切回去主線程臭埋,對于使用接口的人來說完全不需要考慮線程同步的問題踪央,只需要處理好業(yè)務(wù)邏輯就行了,對于開發(fā)效率的提升是非常明顯的瓢阴。有興趣的可以看看Node.js的事件驅(qū)動畅蹂,非阻塞IO模型。Node.js的接口就完全是異步的炫掐,而且居然連能創(chuàng)建線程的接口都沒有魁莉。
最小暴露原則
最小暴露原則說的就是,僅在頭文件中暴露外部需要使用的類型募胃、宏旗唁、函數(shù)和全局變量,所有不需要給外部使用的都不放在對外公開的頭文件中痹束,比如我定義一個模塊會是這樣的:
Person.c
Person.h
PersonInternal.h
所有不想對外暴露的接口检疫,僅對內(nèi)暴露的接口都定義在Inter.h頭文件中。
- 私有實現(xiàn)
對象中如果有些字段覺得沒必要暴露出來的可以選擇私有實現(xiàn)祷嘶,如下定義一個PersonPri_t類型屎媳,但是在頭文件里面只有聲明,實現(xiàn)定義在.c文件中论巍。
typedef struct PersonPri_st PersonPri_t;
typedef struct Person_st
{
char name[20];
int age;
void (*setName)();
void (*setAge)();
PersonPri_t *pri;
}Person_t