1. 優秀代碼
什么樣的代碼是優秀的代碼?這個概念目前還沒有人能確切的定義。然而廣大的程序設計人員普遍都認可的一系列標準基本上從各個層面提出了對優秀代碼的要求。最近在讀《編程匠藝》,覺得文章都寫的很出色,相比之前讀《卓有成效的程序員》,該作從代碼本身講述了編程“那些事”。深受啟發,寫寫讀后總結。
http://gxgonline.bokee.com/679308.html 這里有一段“優秀代碼標準”,具體限于篇幅不再贅述。只是從自己這個編程菜鳥的角度,去談談對編寫優秀代碼的理解。正如每個人心中都有一個衡量標準一樣,我對優秀代碼也有一個衡量的標準:結果正確的,形式優美的,性能高效的,易重用和健壯。當然結果的正確性和程序本身的性能取決于算法設計,這是另一回事,不是本文所討論的問題,本文只討論與代碼的形式和結構有關的問題。
2. 編寫優秀代碼
優秀的代碼首先具有優美的樣式,我想這點是所有編程人員所共識的。沒有人愿意讀晦澀難懂的代碼,相反,如果放在你面前的代碼結構清晰,樣式優美,那么讀程序的人想必也讀的心曠神怡了。要記住一點,代碼寫出來并不是永遠給自己看的,而且就算給自己看,也不是一直都能看的懂的。那么如何寫出形式優美的代碼呢?第一點應該注意的就是代碼樣式,什么是好的樣式:一致,傳統,簡潔。
一致的代碼主要指程序中代碼縮進要一致,括號的位置要一致……其實不用細數,所有的一切關于代碼的東西,都要一致。正像那些大師們說的,“挑選一種適合你的風格,然后堅持下去”,這就是一致。那么好了,有哪些好的風格呢,經典的括號位置有K&R風格和懸掛式風格,具體是什么樣的,見下表:
表1. 括號風格
另外一個要一致的東西就是命名了,就像文章由斷落、章句和字詞組成一樣,代碼也由變量、函數、類型等等構成。合理一致的命名對于代碼的樣式同樣重要。“恰當地命名的關鍵是準確理解你所命名的對象。只有這樣你才能給它起一個有意義的名稱。如果你不能為某個對象起一個號名字,那么你就不會真的知道這個對象是什么或者這個對象真的有必要存在嗎?”
接下來說說名字,名字首先得是描述性的,單純的字母命名是我們編程最常用的,為什么如此,我把它歸結為我們初學編程時那一點點的懶惰,隨便的定義int a, int b這樣的變量,完成著短小的程序。但是當程序漸漸的大起來,文件漸漸的多起來,這樣的變量名字顯得那么的渺小和蒼白而最終毫無意義。所以如果無法描述一個對象,就不要為它起名字。第二,技術上是正確的,關于這點,由于現在的編程工作大多是在IDE下進行,因此這部分的檢查工作我們程序員都交給了IDE,當然如果命名滿足了第一條——具有描述性——那么也基本不會違反技術規范了。第三,恰當的,恰當的命名體現在長度和格調上,也許你為一個變量起了一個具有描述性的名字并且也是技術上正確的(且符合語言習慣),但是它卻長達30個字符(theNumberOfImagesInMyComputer)或者只有1個字符(a,b等等),這樣的做法是不好的。對于命名格調的理解,我想這是一個文化背景的問題,有的人很喜歡用foo或bar這樣的詭異名字,這樣做其實違反了“傳統”原則,這個稍后會提到。關鍵需要記住的“了解你的語言的命名規則。但更重要的是看,了解這種語言的習慣用法。公共命名約定是什么?應用這些語言習慣和約定。”
編碼要符合標準,這樣就會與別人的一致,同樣也會與自己過去寫的代碼一致。這種縱向橫向的一致往往從一個編程標準開始。當然編程標準的制定也應該是盡量符合傳統,這就說到第二個問題了。
不要去新鮮的創造樣式,而應該去遵循傳統。這樣可以確保別人讀懂你的代碼,同時不會讓未來的自己和其他人犯暈。我理解的傳統更像是一種大眾化,就像上面講到命名時說的,不要用詭異的名字,foo和bar不是什么好的選擇。
簡潔講的更多是一種補充。假設你自己訂立一個標準的話,你遵循了傳統,也一直堅持使用它,但是由于你的天才你把編程標準弄的過于復雜,這樣可是苦了其他的程序員了。所以,能簡潔是盡量簡潔,不要追求華而不實的代碼樣式。記得自己以前寫代碼,總寫一些類似于“a[index++] = some();”這樣的代碼。現在想想,還是改成“a[index] = some(); index++;”這樣更簡潔。簡潔不意味著代碼行數少,而是讓人看著舒服。
我認為,程序員寫代碼就像作家寫文章一樣,清新的結構,行云流水的成文往往能令讀者身臨其境。而我們作為讀者讀代碼的過程中,也無不希望代碼優美,就像是在看優美的散文一樣。其實說了這么多,無論是縮進或者括號位置更甚者是命名,這都是在增強我們代碼的可讀性。當你讀代碼像讀文章一樣,那時你就是一個專業程序員了,當你寫代碼能像寫文章時,那時你就是一個優秀程序員了。
當然代碼的可讀性離不開注釋,注釋本身不是代碼,但是確是優秀代碼所必不可少的東西。“注釋的目標讀者是人,而不是計算機。”注釋是一種內部的文檔化機制。什么時候寫注釋呢?這個問題其實很好回答:當你需要解釋代碼為什么時,去寫注釋。那么如何寫呢?記住幾條法則,確保遵循這些法則,那么你也能寫出美妙的注釋。第一,不要描述代碼,“一個事實——一個源頭,不要在注釋中重復代碼。”因為本身你是在解釋為什么而寫代碼,這是一種目的解釋,而不是過程描述。所以如果你有解釋代碼的注釋習慣,那么,摒棄它吧(當然個別難以理解的算法除外)。第二,不要取代代碼,“當你發現自己在編寫密密麻麻的注釋來解釋你的代碼時,趕快停下來。”注釋的字數不會太多,大量的注釋看著也讓人頭痛。第三,確保注釋有用,“想一想你在注釋中寫些什么,不要不動腦筋就輸入。寫完之后要在代碼的上下文中回顧一遍這些注釋。它們是否包含了正確的信息?”確保注釋是有價值的,是真的,是容易理解和清晰明了的,不要用一些含混不清的俚語或個人幽默。最后,避免分心。注釋中不應該包含那些陳年舊事,也不應該用各種ASCII圖形來美化,因為字體寬度變化的多種編輯器可能讓你這種美化丑陋不堪。同時我也不是很支持在代碼塊的結尾用“//end if(a<1)”這樣的注釋來解釋。清晰的縮進和括號已經可以解釋一切了。
說到底,清晰代碼結構,良好的編碼風格和注釋,都是在提高我們代碼的可讀性。而我們寫這樣代碼的目標就是代碼的自文檔化。我們總會在寫完代碼的時候,花費大量的時間去寫文檔,這其實從架構層面來講,增大了耦合,誰的耦合?代碼和文檔的。代碼一經改變,文檔相應也要改變。這樣的復雜關聯總是耗費了編程人員大量的精力,那么為什么不去在代碼本身的形式上花些功夫呢?如果我們都能編寫可以閱讀的代碼,人性化,簡單易懂,那么文檔還需要嗎?就像我們看書籍作品,很少見哪本小說還配著一部說明文檔的。
最后,再說一說代碼的易用性和健壯性。除了美觀可讀這些外部特性外,編寫代碼還應該具有易用性和健壯性。《Code Craft》里提倡使用防御性編程,并給出了防御性編程的一些技巧。當然我并不是防御性編程的fans,也不是契約式編程的死忠。只是從健壯性的角度看,防御性編程是必須的。然而過多的防御降低了代碼的效率,有時候甚至是美觀,這樣的代碼也許使問題復雜了。不管怎么說,代碼的易用和健壯是經驗積累的體現,怎樣寫出這樣的代碼?好吧,沒有捷徑,大量的編寫代碼并閱讀其他人寫的優秀的代碼。漸漸的,你會發現,你的代碼也可以同樣的優秀。
3. 小結
本文主要從代碼的形式上講述了編寫優秀代碼應該注意的一些細節。當然,代碼的核心——算法沒有涉及。最后的一些關于代碼健壯性的認識也是拙劣有余。作為一個編寫代碼愛好者,寫出優秀的代碼是我們追求的目標。培養良好的編程習慣,掌握更多的編程技巧,不斷的編寫和閱讀……一點心得,一點總結。
4. 參考文獻及推薦閱讀
[1]http://gxgonline.bokee.com/679308.html
[2]編程匠藝, Pete. Goodliffe著,韓江、陳玉譯
[3]如何編寫優秀代碼,http://hi.baidu.com/08027/blog/item/bc30cefe78e1bf305d6008ef.html