歐內(nèi)斯特.海明威(Erners Hemingway) :
嚴(yán)肅是寫作必備的兩大要素之一。另一個(gè),很不辛,是天分。
我們編寫代碼,是要表達(dá)一套清晰的指令,它不僅僅面向電腦,也面向修改、拓展和維護(hù)這些指令的人。
在軟件產(chǎn)品的生命周期內(nèi),代碼將不斷地被修改、拓展和維護(hù)。所以文檔化代碼是一件有益的工作。
一、如何代碼文檔化
編寫大量關(guān)于代碼的文檔
有時(shí)可以見(jiàn)到由設(shè)計(jì)規(guī)范、實(shí)現(xiàn)說(shuō)明、維護(hù)指南和風(fēng)格指南支持的軟件系統(tǒng),這種做法會(huì)導(dǎo)致如下問(wèn)題:
(a).除了編寫程序外,要耗費(fèi)大量時(shí)間去編寫文檔和閱讀文檔;
(b).所有文檔必須隨代碼更改而及時(shí)更新,否則會(huì)導(dǎo)致危險(xiǎn)的錯(cuò)誤和產(chǎn)生誤導(dǎo)信息;
(c).大量文檔的管理也是一大問(wèn)題;
(d).由于信息沒(méi)有放在代碼旁,一些重要信息容易被忽略。使用詳細(xì)的代碼注釋
這是一種可選方案,但容易寫出一堆毫無(wú)創(chuàng)造性的逐條注釋。最理想的方案:寫自文檔化的代碼
我們要相信:唯一能完整并正確地描述代碼的文檔是代碼本身。
我們應(yīng)當(dāng)盡可能地編寫可讀性高的代碼,這種代碼本身易于理解,甚至不需要任何外部文檔或注釋加以說(shuō)明。如下面這段代碼:
int fibonacci(int position)
{
if (position < 2)
{
return 1;
}
int previousButOne = 1;
int previous = 1;
int answer = 2;
for (int i = 2; i < position; i++)
{
previousButOne = previous;
previous = answer;
answer = previousButOne + previous;
}
return answer;
}
這段代碼沒(méi)有注釋,但我們很容易讀懂它。
二、編寫"自文檔化"代碼的技能:
-
使用好的樣式,編寫簡(jiǎn)單的代碼。
(a).程序流程分明。錯(cuò)誤情況不會(huì)擾亂程序正常執(zhí)行流程;
(b).避免使用過(guò)多的嵌套語(yǔ)句;
(c).謹(jǐn)慎地優(yōu)化代碼,讓它清晰地表達(dá)基礎(chǔ)算法。 -
選擇有意義的名稱
具體見(jiàn)《讀《編程匠藝—編寫卓越的代碼》:命名》 -
用多個(gè)簡(jiǎn)單的函數(shù)代替復(fù)雜的函數(shù)
(a).一個(gè)函數(shù),一種操作;
(b).減少任何出人意料的副作用,它們會(huì)要求額外文檔的;
(c).保持簡(jiǎn)短,短小的函數(shù)更易于理解。 -
選擇描述性的類型。
(a).定義永遠(yuǎn)不變的值為常值變量(C/C++使用const);
(b).定義非負(fù)變量為無(wú)符號(hào)類型;
(c).使用枚舉描述一組相關(guān)值;
(d).選擇適當(dāng)類型。C/C++中,將值的大小放入size_t變量,將指針?lè)湃雙trdiff_t變量。 -
命名常量
對(duì)于常量字符串和數(shù)字,不要直接在代碼中使用。如if (counter == 72)這樣的代碼會(huì)讓人無(wú)法理解,神奇數(shù)字72是什么,if的目的是什么呢,我們不知道;但如果寫成
const size_t bananas_per_cake = 72;
....
if (count == bananas_per_cake)
{
//做香蕉蛋糕
}
就清晰很多。容易明白數(shù)字72代表每個(gè)蛋糕需要的香蕉數(shù)目,而且如果需要改變數(shù)字72,只需做一個(gè)改動(dòng)即可。
-
強(qiáng)調(diào)重要的代碼
(a).在類中按一定數(shù)序進(jìn)行聲明:用戶需要的公共信息在前,對(duì)用戶不重要的私有的實(shí)現(xiàn)細(xì)節(jié)放在后面;
(b).盡可能隱藏所有不重要信息。C++中,使用pimpl idiom實(shí)現(xiàn)類的實(shí)現(xiàn)細(xì)節(jié); -
盡可能通過(guò)語(yǔ)言結(jié)構(gòu)將對(duì)象分組。
C++/C#中通過(guò)命名空間分組;Java中使用包分組。 -
提供文件頭
在文件頂部放置一個(gè)注釋塊,簡(jiǎn)述文件內(nèi)容、所屬項(xiàng)目及版權(quán)聲明* -
恰當(dāng)?shù)靥幚礤e(cuò)誤
(a). 在最恰當(dāng)?shù)纳舷挛闹刑幚礤e(cuò)誤;
(b).不要返回?zé)o意義的錯(cuò)誤信息。 -
編寫有意義的注釋
先考慮優(yōu)化代碼(如換一個(gè)名字或新增一個(gè)下屬函數(shù))。只有在你無(wú)法以任何其他方式來(lái)提高代碼清晰度的情況下,再添加恰當(dāng)?shù)淖⑨尅?/li>
三、實(shí)用的自文檔化方法
《編程風(fēng)格的元素》中Kernighan和Plaugher:
不要對(duì)糟糕的代碼進(jìn)行文檔化——重寫這些代碼。
A. 文學(xué)編程
著名計(jì)算機(jī)科學(xué)家Donald Knuth在他的1992年出版的《文學(xué)編程》提出了一種極端的自文檔化代碼技巧——文學(xué)編程,并在書中詳細(xì)描述了這種編程方法。
B.文檔化工具
利用工具通過(guò)分離特殊格式的注釋塊,從源代碼生成文檔。
可以文檔化你編寫的任何代碼:類、類型、函數(shù)、參數(shù)、標(biāo)志、變量等,還能方便地獲取大量信息:
- 指定版本信息
- 記錄創(chuàng)建日期
- 交叉引用信息
- 將舊代碼標(biāo)記為過(guò)時(shí)
- 為快速引用提供簡(jiǎn)短的對(duì)照表
- 描述每個(gè)函數(shù)的參數(shù)
流行的代碼文檔化工具有:
- Java的Javadoc
- C#的NDoc
- 適用于多種編程語(yǔ)言的Doxygen
一些有用的經(jīng)驗(yàn):
- 對(duì)于每個(gè)公共可見(jiàn)的對(duì)象,都編寫一兩句描述,不要冒險(xiǎn)寫太多文字。
- 如果變量或參數(shù)用途不明顯,請(qǐng)簡(jiǎn)要說(shuō)明;如果它們的名字表達(dá)的很清楚,則不用添加描述
- 如果函數(shù)的一些參數(shù)用于輸入,另一些用于輸出,需明確說(shuō)明
- 不要文檔化任何一個(gè)函數(shù)的前置和后置條件
四、總結(jié)
埃德溫·施羅斯伯格(Edwin Schlossberg):
寫作的技巧就是創(chuàng)建一個(gè)上下文語(yǔ)境,別人在其中思考
代碼本身就是一種交流媒介,考慮那些需要維護(hù)代碼的程序員們的需求,努力寫出清晰的代碼——用最少最恰當(dāng)?shù)淖⑨?文檔,用"自文檔化"的代碼。
內(nèi)容相關(guān)
Pimpl idiom
Pimopl是C++開發(fā)中經(jīng)常使用的一種慣用法,其原理主要是將對(duì)定義的依賴轉(zhuǎn)換為對(duì)聲明的依賴,通過(guò)前向聲明,達(dá)到接口與實(shí)現(xiàn)的分離的效果,并將編譯時(shí)文件間的依賴降到最低,從而大大縮短程序編譯的時(shí)間。簡(jiǎn)單點(diǎn)說(shuō),就是將一個(gè)類分割為兩個(gè)類,一個(gè)提供接口,一個(gè)負(fù)責(zé)實(shí)現(xiàn),既能最小化編譯依賴,又能接口與實(shí)現(xiàn)分離。Pimpl的實(shí)現(xiàn)方式有兩種,第一種是讓實(shí)現(xiàn)類成為接口類的一個(gè)私有指針成員變量;另一種實(shí)現(xiàn)方式則是通過(guò)繼承的方式實(shí)現(xiàn):讓接口類成為抽象基類,這個(gè)基類包含一個(gè)返回該類指針的靜態(tài)方法,該方法通過(guò)實(shí)現(xiàn)類的構(gòu)造函數(shù)實(shí)現(xiàn),實(shí)現(xiàn)類繼承自這個(gè)抽象基類,并實(shí)現(xiàn)基類描述的接口?!菏纠刹榭?a target="_blank" rel="nofollow">PIMPL IDIOM & FAST PIMPL』
寫作與編程
關(guān)于寫作技巧的提高,有一個(gè)簡(jiǎn)單的原則:讀的書越多,寫得就越好。用批判的眼光閱讀著名作者的作品,可以使你學(xué)會(huì)如何分辨好壞,還能從中學(xué)到新的技巧和習(xí)慣用法。類似的,如果你閱讀大量?jī)?yōu)秀的代碼,那么你也會(huì)成為一位更出色的程序員。你會(huì)在編寫代碼時(shí)不自覺(jué)地運(yùn)用好的技巧,一旦寫出糟糕的代碼,你會(huì)立馬警覺(jué),甚至渾身不自在。