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