經(jīng)常聽見別人說面向?qū)ο蟮某绦蛟O(shè)計,以前在學(xué)校上課的時候,也有開面向?qū)ο蟪绦蛟O(shè)計這門課??墒遣恍业氖?,這些都是以C++,甚至VC++為基礎(chǔ)的。而更加不幸的是,多年以來我一直是一個C的使用者。在學(xué)校的時候,我主要做的是硬件上的驅(qū)動層,和底層功能層。在工作以后,又做的是手機上的軟件開發(fā),所有這些都是和C離不開的。雖然我不得不說,C++是一門很好的語言,但是它的編譯速度,代碼效率,編譯后的代碼大小都限制了它在嵌入式上的應(yīng)用。(但現(xiàn)在的嵌入式CPU越來越快,內(nèi)存容量變大。我覺得用C++也應(yīng)該沒有什么問題。這使我覺得似乎是嵌入式編譯器的限制。雖然菲利普和TI好像都有C++的編譯器,但是似乎沒人用這個。難道是太貴了? 但不管怎么說,嵌入式應(yīng)用中,C語言的普遍使用是肯定的)
那么在面向過程的時代產(chǎn)生的C語言能否使用面向?qū)ο蟮乃枷肽??我認為是肯定可以的,C++不過是在語言級別上加入了對對象的支持,同時提供了豐富的對象庫。而在C語言下,我們只好自力更生了。
一、 面向?qū)ο笏枷氲哪康氖强蚣芑?,手段是抽?BR> 相信很多人都明白面向?qū)ο笾v了什么:類,抽象類,繼承,多態(tài)。但是是什么原因促使這些概念的產(chǎn)生呢?
打個比方說:你去買顯示器,然而顯示器的品牌樣式是多種多樣的,你在買的過程中發(fā)生的事情也是不可預(yù)測的。對于這樣的事情,我們在程序語言中如何去描述呢。面向?qū)ο蟮乃枷刖褪菫榱私鉀Q這樣的問題。編寫一個程序(甚至說是一個工程),從無到用是困難的,從有到豐富是更加困難的。面向?qū)ο髮⒊绦虻母鱾€行為化為對象,而又用抽象的辦法將這些對象歸類(抽象),從而將錯綜復(fù)雜的事情簡化為幾個主要的有機組合(框架化)。
其實我們的身邊很多東西都是這樣組成的:比如說電腦:電腦是由主板,CPU加上各種卡組成的。這就是一個框架化。而忽略不同的CPU,不同的主板,不同的聲卡,網(wǎng)卡,顯卡的區(qū)別,這就是抽象。再比如說現(xiàn)在的教育網(wǎng):是由主核心節(jié)點:清華,北大,北郵等幾個,然后是各個子節(jié)點,依次組成了整個教育網(wǎng)網(wǎng)絡(luò)。
所以我覺得面向?qū)ο蟮木幊趟枷刖褪牵阂粋€大型工程是分層次結(jié)構(gòu)的,每層又由抽象的結(jié)構(gòu)連接為整體(框架化),各個抽象結(jié)構(gòu)之間是彼此獨立的,可以獨立進化(繼承,多態(tài))。層次之間,結(jié)構(gòu)之間各有統(tǒng)一的通訊方式(通常是消息,事件機制)。
二、 以前 C 語言編程中常用的“面向?qū)ο蟆狈椒?BR> 其實C語言誕生以來,人們就想了很多辦法來體現(xiàn)“面向?qū)ο蟆钡乃枷?。下面就來說說我所知道的方法。先說一些大家熟悉的東東,慢慢再講詭異的。呵呵
1. 宏定義:
有的人不禁要問,宏定義怎么扯到這里來了,我們可以先看一個簡單的例子:
#define MacroFunction Afunction
然后在程序里面你調(diào)用了大量的AFunction,但是有一天,你突然發(fā)現(xiàn)你要用BFunction了,(不過AFunction又不能不要,很有可能你以后還要調(diào)用),這個時候,你就可以#define MacroFunction Bfunction來達到這樣的目的。
當(dāng)然,不得不說這樣的辦法是too simple,sometime naïve的,因為一個很滑稽的問題是如果我一般要改為BFunction,一半不變怎么辦? 那就只好查找替換了。
2. 靜態(tài)的入口函數(shù),保證函數(shù)名相同,利用標(biāo)志位調(diào)用子函數(shù):
這樣的典型應(yīng)用很多,比如說網(wǎng)卡驅(qū)動里面有一個入口函數(shù)Nilan(int FunctionCode,Para*)。具體的參數(shù)是什么記不清楚了。不過NiLan的主體是這樣的:
Long Nilan(int FunctionCode,Para*){
Switch(FunctionCode){
Case SendPacket: send(….)
Case ReceivePacket: receive(…)
…..
}
寫到這里大家明白什么意思了吧。保證相同的函數(shù)名就是說:網(wǎng)卡驅(qū)動是和pNA+協(xié)議?;ミB的,那么如何保證pNA+協(xié)議棧和不同的驅(qū)動都兼容呢,一個簡單的辦法就是僅僅使用一個入口函數(shù)。通過改變?nèi)绻瘮?shù)的參數(shù)值,來調(diào)用內(nèi)部的各個函數(shù)。這樣的做法是可以進化的:如果以后想調(diào)用新的函數(shù),增加相應(yīng)的函數(shù)參數(shù)值就好了。如果我們將網(wǎng)卡驅(qū)動和pNA+協(xié)議??醋鲀蓚€層的話,我們可以發(fā)現(xiàn):
層與層之間的互連接口是很小的(這里是一個入口函數(shù)),一般是采用名字解析的辦法而不是具體的函數(shù)調(diào)用(利用FunctionCode調(diào)用函數(shù),Nilan僅僅實現(xiàn)名字解析的功能) ――!接口限制和名字解析
接口限制:層與層之間僅僅知道有限的函數(shù)
名字解析:層與層之間建立共同的名字與函數(shù)的對應(yīng)關(guān)系,之間利用名字調(diào)用功能。
3.CALLBACK函數(shù)。
我覺得這是C語言的一個創(chuàng)舉,雖然它很簡單,就象如何把雞蛋豎起來一樣,但是你如果沒想到的話,嘿嘿。如果說靜態(tài)入口函數(shù)實現(xiàn)了一個可管理的宏觀的話,CallBack就是實現(xiàn)了一個可進化的微觀:它使得一個函數(shù)可以在不重新編譯的情況下實現(xiàn)功能的添加!但是在早期的時候,也有蠻多人持反對態(tài)度,因為它用了函數(shù)指針。函數(shù)指針雖然靈活,但是由于它要訪問內(nèi)存兩次才可以調(diào)用到函數(shù),第訪問函數(shù)指針,第二次才是真正的函數(shù)調(diào)用。它的效率是不如普通函數(shù)的。但是在一個不太苛刻的環(huán)境下,函數(shù)調(diào)用本身就不怎么耗時,函數(shù)指針的性能又不是特別糟糕,使用函數(shù)指針其實是一個好的選擇。但是函數(shù)指針除了性能,麻煩的地方就是會導(dǎo)致程序的“支離破碎”。試想:在程序中,你讀到一個函數(shù)指針的時候,如果你愣是不知道這個函數(shù)指針指向的是哪個函數(shù),那個感覺真的很糟糕。(可以看后面的文章,要使用先進的程序框架,避免這樣的情況)
那么在面向過程的時代產(chǎn)生的C語言能否使用面向?qū)ο蟮乃枷肽??我認為是肯定可以的,C++不過是在語言級別上加入了對對象的支持,同時提供了豐富的對象庫。而在C語言下,我們只好自力更生了。
一、 面向?qū)ο笏枷氲哪康氖强蚣芑?,手段是抽?BR> 相信很多人都明白面向?qū)ο笾v了什么:類,抽象類,繼承,多態(tài)。但是是什么原因促使這些概念的產(chǎn)生呢?
打個比方說:你去買顯示器,然而顯示器的品牌樣式是多種多樣的,你在買的過程中發(fā)生的事情也是不可預(yù)測的。對于這樣的事情,我們在程序語言中如何去描述呢。面向?qū)ο蟮乃枷刖褪菫榱私鉀Q這樣的問題。編寫一個程序(甚至說是一個工程),從無到用是困難的,從有到豐富是更加困難的。面向?qū)ο髮⒊绦虻母鱾€行為化為對象,而又用抽象的辦法將這些對象歸類(抽象),從而將錯綜復(fù)雜的事情簡化為幾個主要的有機組合(框架化)。
其實我們的身邊很多東西都是這樣組成的:比如說電腦:電腦是由主板,CPU加上各種卡組成的。這就是一個框架化。而忽略不同的CPU,不同的主板,不同的聲卡,網(wǎng)卡,顯卡的區(qū)別,這就是抽象。再比如說現(xiàn)在的教育網(wǎng):是由主核心節(jié)點:清華,北大,北郵等幾個,然后是各個子節(jié)點,依次組成了整個教育網(wǎng)網(wǎng)絡(luò)。
所以我覺得面向?qū)ο蟮木幊趟枷刖褪牵阂粋€大型工程是分層次結(jié)構(gòu)的,每層又由抽象的結(jié)構(gòu)連接為整體(框架化),各個抽象結(jié)構(gòu)之間是彼此獨立的,可以獨立進化(繼承,多態(tài))。層次之間,結(jié)構(gòu)之間各有統(tǒng)一的通訊方式(通常是消息,事件機制)。
二、 以前 C 語言編程中常用的“面向?qū)ο蟆狈椒?BR> 其實C語言誕生以來,人們就想了很多辦法來體現(xiàn)“面向?qū)ο蟆钡乃枷?。下面就來說說我所知道的方法。先說一些大家熟悉的東東,慢慢再講詭異的。呵呵
1. 宏定義:
有的人不禁要問,宏定義怎么扯到這里來了,我們可以先看一個簡單的例子:
#define MacroFunction Afunction
然后在程序里面你調(diào)用了大量的AFunction,但是有一天,你突然發(fā)現(xiàn)你要用BFunction了,(不過AFunction又不能不要,很有可能你以后還要調(diào)用),這個時候,你就可以#define MacroFunction Bfunction來達到這樣的目的。
當(dāng)然,不得不說這樣的辦法是too simple,sometime naïve的,因為一個很滑稽的問題是如果我一般要改為BFunction,一半不變怎么辦? 那就只好查找替換了。
2. 靜態(tài)的入口函數(shù),保證函數(shù)名相同,利用標(biāo)志位調(diào)用子函數(shù):
這樣的典型應(yīng)用很多,比如說網(wǎng)卡驅(qū)動里面有一個入口函數(shù)Nilan(int FunctionCode,Para*)。具體的參數(shù)是什么記不清楚了。不過NiLan的主體是這樣的:
Long Nilan(int FunctionCode,Para*){
Switch(FunctionCode){
Case SendPacket: send(….)
Case ReceivePacket: receive(…)
…..
}
寫到這里大家明白什么意思了吧。保證相同的函數(shù)名就是說:網(wǎng)卡驅(qū)動是和pNA+協(xié)議?;ミB的,那么如何保證pNA+協(xié)議棧和不同的驅(qū)動都兼容呢,一個簡單的辦法就是僅僅使用一個入口函數(shù)。通過改變?nèi)绻瘮?shù)的參數(shù)值,來調(diào)用內(nèi)部的各個函數(shù)。這樣的做法是可以進化的:如果以后想調(diào)用新的函數(shù),增加相應(yīng)的函數(shù)參數(shù)值就好了。如果我們將網(wǎng)卡驅(qū)動和pNA+協(xié)議??醋鲀蓚€層的話,我們可以發(fā)現(xiàn):
層與層之間的互連接口是很小的(這里是一個入口函數(shù)),一般是采用名字解析的辦法而不是具體的函數(shù)調(diào)用(利用FunctionCode調(diào)用函數(shù),Nilan僅僅實現(xiàn)名字解析的功能) ――!接口限制和名字解析
接口限制:層與層之間僅僅知道有限的函數(shù)
名字解析:層與層之間建立共同的名字與函數(shù)的對應(yīng)關(guān)系,之間利用名字調(diào)用功能。
3.CALLBACK函數(shù)。
我覺得這是C語言的一個創(chuàng)舉,雖然它很簡單,就象如何把雞蛋豎起來一樣,但是你如果沒想到的話,嘿嘿。如果說靜態(tài)入口函數(shù)實現(xiàn)了一個可管理的宏觀的話,CallBack就是實現(xiàn)了一個可進化的微觀:它使得一個函數(shù)可以在不重新編譯的情況下實現(xiàn)功能的添加!但是在早期的時候,也有蠻多人持反對態(tài)度,因為它用了函數(shù)指針。函數(shù)指針雖然靈活,但是由于它要訪問內(nèi)存兩次才可以調(diào)用到函數(shù),第訪問函數(shù)指針,第二次才是真正的函數(shù)調(diào)用。它的效率是不如普通函數(shù)的。但是在一個不太苛刻的環(huán)境下,函數(shù)調(diào)用本身就不怎么耗時,函數(shù)指針的性能又不是特別糟糕,使用函數(shù)指針其實是一個好的選擇。但是函數(shù)指針除了性能,麻煩的地方就是會導(dǎo)致程序的“支離破碎”。試想:在程序中,你讀到一個函數(shù)指針的時候,如果你愣是不知道這個函數(shù)指針指向的是哪個函數(shù),那個感覺真的很糟糕。(可以看后面的文章,要使用先進的程序框架,避免這樣的情況)

