表達:用好代碼的肢體語(yǔ)言
Spacesoft【暗夜狂沙】
編程從本質(zhì)上說(shuō)是一種表達。實(shí)際上,編程的過(guò)程就是我們使用一種語(yǔ)言向機器表述一個(gè)工作以及其完成的步驟,讓機器來(lái)進(jìn)行的過(guò)程。也就是說(shuō):軟件開(kāi)發(fā)看起來(lái)似乎應該是一件人和機器溝通的工作,就像那位穿黑衣服戴黑墨鏡的黑客尼奧干的一樣酷。
但是,我們遇到的項目往往太龐大、太復雜,于是我們每天更多的是在試圖和人交流——寫(xiě)文檔闡述我們的需求,用注釋描述我們的設計,用圖表表示系統的結構。也就是說(shuō):我們每天要為我們的思想維護兩套描述:代碼和文檔。而且我們還必須時(shí)時(shí)小心,保證它們一致。然而在比較大的項目里,文檔的數目有時(shí)候會(huì )多得簡(jiǎn)直可怕,而假如這個(gè)項目的文檔管理又有點(diǎn)混亂的時(shí)候,文檔就變成了災難。
當用大量文檔作為條條框框保障起來(lái)的規矩把眾人弄得苦不堪言時(shí),革命發(fā)生了。有人喊出了口號:“源代碼就是設計”。是啊,當文檔和注釋作為必要的輔助出現在開(kāi)發(fā)中時(shí),它們有效的提高了設計的可讀性;而當它們開(kāi)始喧賓奪主,當工程師們每天有效的工作時(shí)間更多的被消耗在不直接產(chǎn)生效益的附件上時(shí),我們就有必要開(kāi)始反思了。我們之所以不用我們日常的語(yǔ)言來(lái)編程,是因為它們有二意性,它們不精確,不易為編譯器所理解。那么我們的設計為什么在用代碼描述之后還要用模糊的自然語(yǔ)言再來(lái)做進(jìn)一步的解釋呢?也許我們的表達方式有問(wèn)題?
這篇文章試圖總結一些用代碼來(lái)表述設計的技巧,當然我還不打算也沒(méi)辦法完全回答剛才提出的問(wèn)題。這些技巧僅僅包括編碼的方式,所以我稱(chēng)之為代碼的“肢體語(yǔ)言”,也就是僅僅采用代碼本身,來(lái)表述你的設計思路和限制。
一.起個(gè)好名字
一個(gè)好名字能夠清晰的描述對象的含義,也可以清晰的描述操作的目的和方式。ctype.h 里面的這個(gè)函數名字就起得很好:
int isalpha( int c );
很明確的告知了函數的意圖。把你代碼里面的
(((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')))這樣的代碼扔掉把,一個(gè)isalpha多么清晰的描述了你要做的動(dòng)作??!
各個(gè)開(kāi)發(fā)團隊的代碼規范往往對命名有比較嚴格的規定。變量的命名往往使用匈牙利規則或者與它類(lèi)似。匈牙利規則很好的利用前綴,使一個(gè)簡(jiǎn)單的變量名攜帶了更多的信息。比如 lpszFormat 這樣一個(gè)變量,熟悉對應編碼規范的人會(huì )很容易看出來(lái)這其實(shí)是一個(gè)字符數組指針。于是他就不用跑到變量的聲明處去看它的類(lèi)型,也不用從上下文推敲,這個(gè)變量到底是CString 還是一個(gè)char *。遵守代碼規范也帶來(lái)同樣的好處:確實(shí)省掉了到處找變量聲明的麻煩。當男生常常起名二狗,女生常常叫做翠花的時(shí)候,我們往往可以省去想翠花是男是女的麻煩。
當然了,起個(gè)好名字,最關(guān)鍵還是要選個(gè)好詞。好詞匯是表達中非常關(guān)鍵的東西,這對寫(xiě)代碼還是寫(xiě)文章都有效。由于我們開(kāi)發(fā)的主流還是用的英文編程,建議變量起名還是不要用漢語(yǔ)拼音了。int ShiBuShiZiFu( int c ); 這樣的命名實(shí)在是別扭,還是能免則免了,我覺(jué)得比英文難懂多了:)
二、利用語(yǔ)言要素描述限制條件
編程語(yǔ)言為了增強表達能力,往往提供了很多的限定,但是我們常常沒(méi)有很好的利用。舉個(gè)Delphi 的例子:
function MyExample (const nParamIn: Integer; var nParamOut:Integer): Integer;
這樣的參數列表就很清晰的告訴了調用者:nParamIn 是輸入用的,nParamOut里面則會(huì )返回一個(gè)數據。
又比如,C++ 中都有這樣的規定:一個(gè)類(lèi)中有純虛函數的話(huà),這個(gè)類(lèi)不能實(shí)例化,于是,你可以用這個(gè)特點(diǎn)來(lái)向基類(lèi)的使用者表達:“你一定要Overload 這個(gè)函數”這樣的要求。
斷言也是說(shuō)明限制的一個(gè)好手段。比如我們要實(shí)現這樣一個(gè)函數:
char *strcpy( char *strDestination, const char *strSource);
我們將把strSource緩沖區中的字符串拷貝到strDestination里面,怎樣表達“strDestination不能為空指針” 這樣一條限制呢?
首先想到的辦法是在函數的開(kāi)始加一句 if (!strDestination) return NULL; 然而這并不是一個(gè)良好的表達。它給調用者傳遞了一個(gè)含混的信息:我碰到問(wèn)題了。至于具體是什么問(wèn)題呢?就不告訴你?;蛘呖梢赃M(jìn)一步:用一個(gè)全局變量式的方法儲存錯誤信息(就像GetLastError()做的那樣),或者用異常。然而,這也不是一個(gè)良好的表達:GetLastError 和異常更加合適的讀者是軟件的最終用戶(hù),而不是程序員。對程序員更友好的方式是在編譯或者調試的時(shí)候就能報告出錯誤的所在。于是我們用斷言。一句assert(strDestination); 明確的告知了使用這個(gè)函數的程序員你需要的限制。
三、使用眾人都了解的表達方式
當一種表達方式為眾人所接受和熟悉的時(shí)候,使用它往往可以省很多口舌,比如自然語(yǔ)言中的成語(yǔ)和典故,就往往被用來(lái)表述一些原本要長(cháng)篇大論才能說(shuō)明白的意思。
模式(Patterns)就是這樣的一種表達方式。在我看來(lái),一個(gè)模式就是總結出一類(lèi)問(wèn)題,以及這些問(wèn)題的經(jīng)典解決之道,最后給這樣的一套東西起個(gè)名稱(chēng)。于是當這樣的思維方式為眾人所認同的時(shí)候,你可以使用模式名稱(chēng)來(lái)指代一些說(shuō)明起來(lái)很麻煩的問(wèn)題和手段。
舉個(gè)例子。我有一個(gè)對象CService,它為另外一個(gè)對象CClient 提供一些服務(wù),兩個(gè)對象都在本地作通信?,F在需求變了,需要把CMyClient 移到其他機器上,但是我又不想改寫(xiě)現有的兩個(gè)模塊。于是乎,我就寫(xiě)了一個(gè)類(lèi)來(lái)管理CClient 對CService的調用。這樣的設計思想怎么描述?洋洋灑灑寫(xiě)上那么幾百字的注釋?zhuān)坎挥?!我把這個(gè)類(lèi)叫做CServiceRemoteProxy 好了。熟悉remote proxy 模式的人一下子就會(huì )理解我要作什么了。當然啦,對模式不熟悉的程序員就麻煩了^_^
所以我覺(jué)得,模式對程序員的重要性在于,模式使得程序員的交流有了更多共同語(yǔ)言。于是了解模式就和當初我們在小學(xué)里學(xué)成語(yǔ)一樣重要了。
另外一個(gè)例子是對公有庫的態(tài)度。很多人對公有的函數庫、類(lèi)庫,甚至編譯器都有不信任感,或者本來(lái)就文人相輕,覺(jué)得用別人的東西不是顯得自己沒(méi)水平?于是就自己實(shí)現一個(gè)自己版本的東西。倘若是為了優(yōu)化或者原來(lái)的公有東西有bug, 那也就罷了,據說(shuō)有牛人自己從Framework到編譯器自己完全作了一套,有任務(wù)的時(shí)候完全在自己的環(huán)境里完成,那未免就有走火入魔之嫌了。這樣的代碼完全沒(méi)有辦法作交流,也就沒(méi)有什么表達的需要,當然不在本文的討論范疇。我覺(jué)得若有可能,還是盡量使用公有的東西,大家都熟悉,也就比較容易理解。怕就怕在自己另外作了一套東西,接口相似,偏偏又有不少東西和而不同,特性大相徑庭。這樣旁人閱讀這樣的代碼,就難免覺(jué)得郁悶,難免要你寫(xiě)文檔了。
比如,我不喜歡string.h 提供的strcpy,于是我就實(shí)現了一個(gè)自己的版本:
int strcpy( char *strDestination, const char *strSource );
還要給個(gè)規定strcpy 成功返回1,失敗返回-1。完了,這回沒(méi)搞頭了,想不寫(xiě)注釋?zhuān)@樣的代碼誰(shuí)看了不糊涂???于是只好老老實(shí)實(shí)寫(xiě)注釋。你說(shuō)這又是何苦捏^0^?
最后,還是要說(shuō)明一下,我在這里強調代碼的表達,不是說(shuō)從此就不再需要注釋和文檔了,有的時(shí)候,一小段注釋?zhuān)f(shuō)明的效果比什么都好,那么我們又何必吝嗇那一點(diǎn)注釋呢?
其實(shí),編程就是表達,精妙的表達可以省去很多口舌,也可以部分的把我們從文檔的海洋里解救出來(lái)。所以用好代碼的肢體語(yǔ)言吧:)
歡迎訪(fǎng)問(wèn)作者的個(gè)人主頁(yè):替換www.alloysoft.com
聯(lián)系客服