從白箱復(fù)用與黑箱復(fù)用_第1頁
從白箱復(fù)用與黑箱復(fù)用_第2頁
從白箱復(fù)用與黑箱復(fù)用_第3頁
從白箱復(fù)用與黑箱復(fù)用_第4頁
從白箱復(fù)用與黑箱復(fù)用_第5頁
免費預(yù)覽已結(jié)束,剩余1頁可下載查看

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、從“白箱復(fù)用與黑箱復(fù)用從“白箱復(fù)用與黑箱復(fù)用.”談到“概要設(shè)計”與“詳細設(shè)計”的劃分及其它(題目女?長J斗膽與魯迅的魏晉風(fēng)度及文章與藥及酒之關(guān)系比試)“封裝、繼承、多態(tài)”是面向?qū)ο缶幊痰娜筇匦??!懊利?、智慧、大方”是(我認(rèn)為的)女人應(yīng)具有的三大優(yōu)點。然而我可以經(jīng)??洫勔粋€女人“最美麗,最智慧,最大方”;但我從來不敢自吹自己寫的程序“最封裝,最繼承,最多態(tài)”。因為“封裝、繼承、多態(tài)”之間屬于相形相克。相形者,指三者中缺少任意一個,則余下二個都將不存在;相克者,則是三者中任意一個如果被發(fā)揮或表現(xiàn)到極限,則余者同樣無法生存。正由于此,可以說,在抽象意義上,任何時候我們進行的程序設(shè)計1,都是力圖在針

2、對當(dāng)前的問題,調(diào)整出這三個特性的各自的最佳“實現(xiàn)度”。這也是所有程序員在不斷培養(yǎng),苦苦追求的設(shè)計能力。什么叫高手、老手?什么叫新手、生手?雖然我懂得“鹽是咸的,味精是甜的,姜是辛",但我始終做不出一手好湯。雖然我也明白“油門、剎車、方向盤”的作用,但當(dāng)舒馬赫在F1賽道上藝術(shù)地操作這三者時,我還在某個坡路上流汗:“又要剎車又要加油門,為難人?。 笔堑?,編程的難點與技藝正在于此。好的程序設(shè)計得讓人幾乎要歸之為“藝術(shù)”;而糟糕的設(shè)計,就像一個蹩腳的廚子走了,留下一桌惡心的菜,你卻不得不去咀嚼它,消化它,其間之苦,真非言語所能表達的。很不幸,我就是這樣一個蹩腳的廚子,說起來“飯菜”也做了10

3、年矣,但依然無法用“封裝、繼承、多態(tài)”做“面向?qū)ο蟆边@一道菜。我這學(xué)習(xí)編程的10載,倒幾近完整地見證了中國軟件開發(fā)行業(yè)發(fā)展的全過程。我也看到了很多程序員都和我一樣,在不斷的摸索、碰壁中緩慢地成長。好在,編程界的泰斗終于感到于心不忍了,開始提供編程界的“菜譜”。這就是如今火熱之至的“設(shè)計模式”2。當(dāng)然,正如萬有引力一直存在著,而不是等到牛頓被蘋果砸了以后才出現(xiàn)。“設(shè)計模式”其實一直存在于優(yōu)秀程序員的設(shè)計里。不過是沒有形諸于文字,而是表現(xiàn)在代碼中。當(dāng)然,也遠遠沒有泰斗們所歸結(jié)出來的模式那樣具備抽象性、概括性和通用性。說到設(shè)計模式,我只是想進一步證明,一個程序員面對不同問題,其駕馭面向?qū)ο笕筇匦缘?/p>

4、能力的重要性。這篇小文不準(zhǔn)備講那23個經(jīng)典模式。我想以最基本的“白箱、黑箱復(fù)用”為例,開始我的論題。首先,不要看到“黑白箱”就想到測試?!袄^承、聚合”分別二者的原意,在不同的編程語言里可能有不同的術(shù)語,這里我們用最直觀的“白箱”,“黑箱”來表述。所引用的代碼,來自于早些日子我在“非程序員”(一個國內(nèi)專講UML的網(wǎng)站)的BBS的發(fā)言。那時有一位可能比我還菜的家伙在上面質(zhì)疑復(fù)用為何要分“白箱”“黑箱”,我一時技癢,上去口水了一番。下面我會通過一個有關(guān)“項目”的故事,將我當(dāng)時用來論證的代碼,用成一次代碼設(shè)計的演進。先得說說“繼承”。C+提供了三種三繼承"private,protected,

5、public”??紤]到JAVAf口C#均只能支持最通用的public繼承,我們這里就僅以此為繼承的標(biāo)準(zhǔn)。所謂的public繼承,字面意思是“公開繼承”。要個比方就是除了“老子”聲明要帶到陰間的財產(chǎn),其它的,它繼承者,都可以獲得,使用。(這是一個蹩腳的比喻,但我想你會承認(rèn)它確實表達了公開繼承和其它繼承的不同L)。“公開繼承”就是一種最常見的白箱復(fù)用的設(shè)計。它表示:“B復(fù)用A的功能,并且B可以了解A的內(nèi)部細節(jié)”。接下來我們講“黑箱復(fù)用”??梢酝茰y,它表示:“B復(fù)用A的功能,但B無法看到A的內(nèi)部細節(jié)”。這在像C#£JAVA這樣不支持私有或保護繼承,也不支持多重繼承的語言,是一種極其常見的設(shè)

6、計。“黑箱復(fù)用”的實現(xiàn)方法是:如果B類想復(fù)用A類的功能,不是從A類派生,而是將A類的對象,聲明成為B類的成員數(shù)據(jù)。嗯,是該來舉一個“實際項目”了。通過演示這個“項目”的實作,我想,就算你是外行人,你也應(yīng)能了解一點:事實上程序員最后用指頭敲寫代碼,其實那不算是編程,真的編程,在于之前他的大腦必須做的分析與設(shè)計(這句話一會兒我會繼續(xù)重復(fù))。(聲明一下,以下故事純屬虛構(gòu))10年的編程生涯,嗯,我的家里有5臺電腦了。書房和臥室各有一間,但因為搬家所以常常換,老婆也有一個(不允許再多,也不允許換)。前三年又有了一個女兒,由于出生“腦香門第”,所以最近小家伙也開始用上我的電腦。這些算是項目的背景和資源。項

7、目的初始需求是這樣:結(jié)蠟后每天晚上我都在書房時和電腦打交道到很晚。于是我老婆認(rèn)為應(yīng)該把那臺筆記本搬到臥室,并責(zé)令我寫個程序,可以實現(xiàn)她在臥室通過電腦向我發(fā)號施令。這樣就有了本項目的產(chǎn)生。作為“客戶”,老婆當(dāng)然希望她可以發(fā)各種各樣的命令;而作為該項目的產(chǎn)品經(jīng)理、技術(shù)架構(gòu)師,開發(fā)負(fù)責(zé)人,代碼撰寫者及測試師于一身的我,當(dāng)然明白正確地引導(dǎo)客戶的需求是一個項目是否成功的最重要前提之一,同時也是對客戶負(fù)責(zé)的表現(xiàn)。我向她解釋了一個無所不包的軟件,首先將讓用戶界面變得繁雜無比,用戶極易操作失誤,而失去耐心;其次是眾多功能之間將互相牽制,導(dǎo)致表面上得到一個無所不包的軟件,實際功能卻強項不強,弱項更弱等等,最后我

8、也委婉地提到了它對開發(fā)周期的可能的影響,以及在開發(fā)和后期維護費用上恐怕會出現(xiàn)幾何級的增長,最后約定是只實現(xiàn)最為常見兩條命令的發(fā)送:a) “老公限N分種內(nèi)來睡覺,否則門將反鎖?!?;b) “腳已洗好,請來端盆。”有了具體的需求描述,這下顯得清楚多了。當(dāng)然,老婆也不吃素的。在具體功能之外,也提出一些速度,性能的要求(這樣就可以杜絕我在限定時間內(nèi)無反應(yīng),會推托是軟件傳送命令太慢等后路),最重要一點也提到該系統(tǒng)應(yīng)具備一定的擴展性,以備今后增加新的命令的要求等等,需求之后是概要設(shè)計,首先我確定通過局域網(wǎng),采用SOCKE睬實現(xiàn)傳輸,而不是通過串口并口紅外線或藍牙。無論是硬件還是軟件,這方面的資源均充備,這算

9、是對開發(fā)資源做了認(rèn)真詳實的調(diào)研并確定。然后我把數(shù)據(jù)流圖畫到了概要設(shè)計。在概要設(shè)計內(nèi),我也決定了將有采用.Net+C#來進行開發(fā),當(dāng)然,也提到了采用Win32+C或C+<JAVA的可能性。最后我也在概要設(shè)計里提出,由于該系統(tǒng)的簡小,在速度,性能,及擴展性并無太多要求,所以應(yīng)將設(shè)計的天平側(cè)向于“易用性”(以博取老婆歡心)。界面上的東西,及第二條命令數(shù)據(jù)的流程,均略。之后開始詳細設(shè)計,秉承概要設(shè)計的思想,我覺得將兩條命令的發(fā)送分別提供。那么要不要采用“多態(tài)”?即是否將發(fā)送兩條命令的發(fā)送動作取同一命名?考慮到以后可能會有新的命令擴展,這里采用多態(tài)會帶來麻煩。所以我在這一步詳細設(shè)計里,放棄多態(tài)特性

10、之一。很顯然,我對“擴展性”雖然沒有完全忽略,甚至是在概要和詳細設(shè)計里都可見“擴展性”的影響,但問題我缺少對“擴展”與“易用”做深入的,更具體的考慮。所以下一步的錯誤的根本,已經(jīng)埋下來。下面我開始提供設(shè)計的偽代碼。假設(shè)C#提供基類Socket,用于在網(wǎng)絡(luò)發(fā)送數(shù)據(jù)。我沒有標(biāo)出函數(shù)參數(shù)data的數(shù)據(jù)類型。但顯然,作為該類的設(shè)計者,他并不知道你要發(fā)送什么樣的數(shù)據(jù)(老婆的命令?老板的命令?)所以這個Send()可以發(fā)送的data肯定是無具體含義的。我們可稱為無格式的數(shù)據(jù)。而我們要發(fā)送的兩個有著具體意義的命令。根據(jù)前面設(shè)計。我們需要為這兩個類分別提供發(fā)送函數(shù)。當(dāng)然,這兩個發(fā)送具體命令的函數(shù),最終肯定是要

11、調(diào)用上述系統(tǒng)提供的Send(渝令來完成實際發(fā)送操作。讓我們來繼承它:是的,我派生了一個新類:“臥室的Socket'。這一命名表征了我心里其實很清楚,我要設(shè)計一個僅供臥室那端的人使用的Socket而我對兩個具體的命令,提供了名字直觀的兩個函數(shù),這也充分體現(xiàn)了我正在按概要設(shè)計的要求進行詳細設(shè)計:請看,通過我對原來的Socket類的派生,以及我對它的Send(冽作的擴展,就在原來抽象的,無特別應(yīng)用方向的類的基礎(chǔ)上,得到一個新類,它有具體應(yīng)用方向,也有具體意義的動作。這比起拿起Socket就直接使用的人(這類人往往是C的高手);或者比起為了“多態(tài)”而“多態(tài)”,從而把新加的兩個函數(shù)也命名為Sen

12、d()的人(這往往是剛接觸C+時幾天的人),我的這個設(shè)計,確實顯得很正確。然而,事實上,這個設(shè)計在面向?qū)ο缶幊痰氖澜缋铮匀皇且粋€拙劣的設(shè)計。在面向?qū)ο缶幊填I(lǐng)域里有經(jīng)驗的程序員,我想已經(jīng)看出其中的欠妥之處。假設(shè)這個項目付諸實施了。當(dāng)老婆的人倒也沒有提出什么擴展。光陰荏苒,結(jié)蠟三年過去了,我們有了一個孩子;然后又是三年過去了,我們的孩子也開始會在電腦上施展她的天才。對這個軟件提出了她看法:“爸爸,應(yīng)該增加一個給我送牛奶的命令”。擴展需求終于出現(xiàn)了。然而,6年過去了,我對這個軟件的記憶是零。沒有看設(shè)計文檔,我就開始看代碼。然后我看到一個類:BedroomSocket我開始使用它,然后我看到它有三個

13、有關(guān)發(fā)送的方法:boolSend();boolSendSackCommanc|();boolSendFootBathCommand();作為一個使用者,我并不想去花時間了解BedroomSocket的具體細節(jié),所以我并不知道其中那個Send()其實是來自Socket這個基類(在實際大型項目開發(fā)中,比如大型ERF;專門寫上層業(yè)務(wù)邏輯的程序員,甚至是沒有權(quán)限可以看到他所使用的類的設(shè)計文檔,更看不到源代碼)。我錯誤地認(rèn)為當(dāng)初設(shè)計BedromSocket時,可能是為了易于對付一些新加的命令,所以提供了一個通用的Send(方法。就這樣,縱然有100個項目經(jīng)理,也無法在第一時間內(nèi)阻止我義無反顧地通過Bed

14、roomSocket的實例來調(diào)用Send(),我會發(fā)現(xiàn)這個Sendfl®在太好用了,什么格式的數(shù)據(jù)都可以發(fā)送。也就這樣,一個項目原來的設(shè)計傾向開始出現(xiàn)偏差。如果這種情況在多人之間出現(xiàn)多次,那么一個項目的設(shè)計風(fēng)格與模式,就將被每個人的理解而肢解成五花八門。不僅僅是在人的方面:理解,改錯,擴展等方面會增加難度,而且對于代碼本身,也必然由于模塊之間接合困難,而需要增加很多附加代碼,最終是程序運行效率低下。你可以怪罪后來者(在這個例子里仍然是“我”),不去深入學(xué)習(xí)需求,概要,設(shè)計文檔。但正如我前面所言,對一個大的項目,會按設(shè)計的層次分成多個子項目;要每一個人都去學(xué)習(xí)每一個項目的詳細設(shè)計文檔,

15、并且最好是從需求開始看起,這是不可能的。再考慮那些中間件的實現(xiàn),通常都凝聚了一個軟企的核心技術(shù)一一這種情況下,分配在實現(xiàn)業(yè)務(wù)邏輯的程序員,沒有權(quán)限去學(xué)習(xí)中間件的具體設(shè)計思路。大家看到的,永遠只是對方的接口。類似于我看到了Bedroom接口透露出來的三個方法,但我不知道這些方法的實現(xiàn)背景。針對這個例子中碰上的問題。我們可以將“白箱復(fù)用”(這里是繼承),改為“黑箱復(fù)用”。在這次設(shè)計中,Socket的對象成為類BedroomCommand的一個成員。類BedroomCommand不再是通過“繼承”來獲得網(wǎng)絡(luò)發(fā)送的能力。而是通過“擁有"一個Socket對象來獲得該對象所有公開的能力。由于So

16、cket的對象sender在BedroomCommand里被聲明為私有(private,或者也可以是保護protected),所以,有關(guān)Socket網(wǎng)絡(luò)發(fā)送的能力,僅有BedroomCommand的設(shè)計者可以直接獲取和使用。這就是“黑箱復(fù)用”的一種常見方法。BedroomCommand的使用者不再需要面對Send()。它所能看見和用到的接口,是BedroomCommand提供三個意義明確的發(fā)送方法:publicboolSendSackCommand,();publicboolSendFootBathCommand,();publicboolSendMilkCommand();這樣,我們就解決前

17、面的問題。我們實現(xiàn)了一個類,它提供了它應(yīng)有的功能,同時杜絕提供它不該有的功能。這正是一個良好的設(shè)計的基本標(biāo)準(zhǔn)。這么看來,是不是黑箱復(fù)用總是白箱復(fù)用來得正確?答案當(dāng)然不是如此,下面我們繼續(xù)給這個設(shè)計制造問題一一想要給“設(shè)計”制造問題,最好的辦法就是修改“需求”了。我們假設(shè)原來的Socket類在除了提供一個公開對外的Send()方法以外,還提供了一個保護的SetOptions(方法。該方法用于對網(wǎng)絡(luò)發(fā)送做一些參數(shù)調(diào)整,以便可以定制出更符合具體要求的網(wǎng)絡(luò)發(fā)送能力。classSocketpublicSend(data);/發(fā)送數(shù)據(jù)protectedSetOptions(,);/定制網(wǎng)絡(luò)條件,;Sock

18、et的設(shè)計者,認(rèn)為SetOptions這一能力是不能直接對外公開,所以SetOptions被設(shè)計為“保護(protected)”。這就使得:除非是Socket本身或它的繼承它的類,否則就無法使用到SetOptions。我們前面講的“白箱復(fù)用”,正是繼承。這就給我們出現(xiàn)一個兩難:如果使用“白箱復(fù)用”,那么我們可以獲得我們想要的SetOptions,但同時我們卻不得不公開了我們不想公開的Send。如果使用“黑箱復(fù)用”,那么們可以不公開Send但卻無法獲得SetOptions的能力。由此產(chǎn)生了“復(fù)合復(fù)用”。(一般來說,SetOptions()在Socket里不會被設(shè)置成virtual,所以在C#里,

19、我們加上new指示符,而在C+,最直接的方法是另取一個名字,比如叫SetMyOptions(),如止匕可以避免關(guān)于編譯器說我們覆蓋了基類同名函數(shù)的小問題。如果SetOptions是virtual類,則不存在該問題。另外,在C+里,base.SetOptions()應(yīng)寫成:Socket:SetOptions()問題得以完美解小。Socket提供的超強能力,只&BedroomCommand的設(shè)計者能獲得,使用。并且通過BedroomCommand的設(shè)計者來決定要對外公開哪些能力。任何一個后來的程序員,無論他是老手還是新手,都不會在使用BedroomCommand上出現(xiàn)偏差。就算是我在下一個6年之后,我也能正確地使用BedroomCommand。這樣的一個設(shè)計,針對當(dāng)前問題,做到既有“粒度”又有“彈性”。由此引申出幾個話題。第一,關(guān)于需求分析、概要設(shè)計、詳細設(shè)計的劃分。概要設(shè)計更多地是在將需求模塊轉(zhuǎn)換為設(shè)計模塊。它從總體上把握了技術(shù)設(shè)計的可行性。著重表達各個設(shè)計模塊之間的靜態(tài)及動態(tài)關(guān)系,并由此確定各設(shè)計模塊之間接口規(guī)劃。一般地說,概要設(shè)計并不需求每個寫代碼的人都參加直接參加設(shè)計。它要求項目技術(shù)負(fù)責(zé)人了解技術(shù)實現(xiàn)上可行性,總體難度;它也要求技術(shù)負(fù)責(zé)人具備把握整個設(shè)計的風(fēng)格、傾向、取舍;但它并不要求技術(shù)負(fù)責(zé)懂得每一個

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論