
在學(xué)校里學(xué)習(xí)時,老師對注釋的要求比較嚴(yán)苛。
你寫代碼的時候不寫注釋,過一段時間你自己都看不懂了,何況別人?
上一句還屬于常規(guī)觀點。某個老師甚至這么說:
C語言,注釋量應(yīng)該和代碼量相當(dāng)。
當(dāng)時,我是心中敬仰的。然而,工作后發(fā)現(xiàn),實際工程領(lǐng)域之中,并沒有那么多注釋。
而且,逐漸發(fā)現(xiàn),大部分情況下不需要、也不應(yīng)該注釋。
由于工作中以Java為主——這真是一個悲傷的故事——以下代碼以Java為例。
1. 錯誤的注釋不如沒有
注釋寫出來,就是給人看的。如果注釋是錯的,就會誤導(dǎo)代碼閱讀者。
/**
* I am a boy, I like girls.
*
* @param someone is a Person who is welcome or not.
*
* @return true if someone is a Person you like.
*/
private boolean isWelcome(Person someone) {
return someone.isBoy();
}
比如上面那段,代碼是個同性戀寫的,注釋的則是異性戀;或者,當(dāng)他寫注釋的時候以為自己性取向很正常,寫代碼的時候心中忽然涌上一陣明悟……
這類注釋在實際工作中碰到很多。在維護(hù)代碼、或者是在以前的基礎(chǔ)上開發(fā)代碼的過程中,往往是進(jìn)行增量修改。某些時候,Method內(nèi)的實現(xiàn)已經(jīng)改了,而代碼注釋未變,就容易出現(xiàn)二者不一致,甚至意義完全相反。
修改代碼時,一定要先修改相關(guān)注釋。這就帶來了維護(hù)成本的提高。
問題是:增加的維護(hù)成本以及誤導(dǎo)幾率,是否能被注釋帶來的收益所抵消?
2. 沒有額外語意的注釋不如不寫
注釋不僅要說明,還必須要有額外語意,或者更簡單地表達(dá)了與代碼實際效果相同的意思。
public class Person {
/** The name of the Person. */
private String name;
/** The phone number of the Person. */
private String phoneNumber;
}
上面那段,這樣的注釋,有什么意義?
-
name不就是name嗎?難道還需要再說一遍,或者翻譯成中文? -
phoneNumber不就是電話號碼嗎?難道要在注釋里拆開,代碼的閱讀者才能看懂?
這類為了注釋而注釋的注釋,除了礙眼,并無意義。而且,還容易出現(xiàn)第1類問題。
比如,以后人類都不用注冊、購買電話號碼了,直接用身份證號作為聯(lián)系方式。上面的代碼需要把phoneNumber改為id,修改者會不會記得改注釋?
他心中一定有一萬頭草泥馬在狂奔:還不如刪掉!
3. 必要的注釋是代碼的失敗
其實,看代碼的人都是碼農(nóng)……呃,我是指,看代碼的人都是能看代碼的人。一般不會存在沒有注釋就看不懂的代碼。
注釋只不過是加速理解代碼的過程。
而且,看了注釋,不代表就不需要看代碼。因為有可能存在第1類問題。
/**
* @return true if this is a girl and she is beautiful.
*/
public boolean isGood() {
int value = this.getValue();
// If the 1st bit of value is 0, means this is a girl.
// If the 2nd bit of value is 0, means this person is beautiful.
if ((value & 0x01) == 0 && (value & 0x02) == 0) {
return true;
} else {
return false;
}
}
以上代碼中,Method的注釋已經(jīng)寫得很清楚了,isGood()是看這個人是不是個美女。
這段注釋的確加速了理解,因為isGood()并不是很直觀。什么樣的好才是好?
但是,為什么不把Method命名為isBeautifulGirl()?表達(dá)了同樣意義的同時,也不用閱讀者跳轉(zhuǎn)到定義位置來看。
其次,在實現(xiàn)中的這段注釋的確加速了理解,不然這個位操作要看很久(視腦年齡而定)。
但是,特么誰準(zhǔn)你在Java里用位操作?!
不管你這個int里存了多少位的信息,為什么不能拆分成多個值?
如果拆分成多個值,每個值都給予恰當(dāng)?shù)拿湟赃m當(dāng)?shù)腗ethod,為什么還需要注釋?
例如以下代碼,需要注釋嗎?
public boolean isBeautifulGirl() {
return isGirl() && isBeautiful();
}
當(dāng)你被自己的代碼逼迫,以致要寫注釋之前,你應(yīng)該問自己:是不是這段代碼寫得太糟了?要么再改改?
4. 千萬不要寫中文注釋
雖然代碼往往都是以英文為基礎(chǔ)的,代碼旁邊配以大段的英文注釋也確實令人抓狂。
但是比英文更令人抓狂的是亂碼!
更令人抓狂的是,怎么轉(zhuǎn)換都是亂碼的亂碼!
/**
* ?????????????????
*/
public void doSomething() {
}
誰能把這個亂碼轉(zhuǎn)回去,看看我注釋里寫了什么?
另外,是不是有一種刪掉的沖動?
好的代碼本身就是注釋
Person person = I.meet();
if (person instanceof Girl) {
Girl she = (Girl) person;
if (she.isBeautiful()) {
if (she.isKind()) {
I.marry(she);
} else if (she.isBitch()) {
I.fuck(she);
} else {
I.play(she);
}
} else {
I.doNothing();
}
} else {
I.stayLonely();
}
以上代碼,雖然if深了一點,但是不需要注釋也完全可讀。哪怕從自然語言的語法角度看也問題多多,不過,代碼的邏輯就在那里,比任何自然語言更準(zhǔn)確地描述了程序的實際運行狀況。
There are a thousand Hamlets in a thousand people's eyes.
一千個讀者就有一千個哈姆雷特。
——莎士比亞(Shakespeare)
如果你強(qiáng)行要把編程語言編譯成自然語言,你能確定你表達(dá)了你想表達(dá)的,你確定你想表達(dá)的就是程序?qū)嶋H運行的?
你確定沒有犯前面的四類問題?
注釋,需要是表達(dá)了和代碼相同涵義(第1類問題)、而又不能是相同語意(第2類問題)、在代碼比較復(fù)雜或難讀(第3類問題)、并且不能用ASCII以外的字符的情況下,寫在代碼旁邊解釋代碼的東西。
(其實一句話就能打敗大多數(shù)中國程序員:你英文寫得比代碼好?)
所以,還是讓一切盡在代碼中吧!
Code is poetry.
log即注釋
在實際的工作中,你可以不寫注釋,但不得不寫log。
注釋是寫給代碼閱讀者看的,并不參與編譯。僅存在與代碼中,編譯后的軟件里并不存在。
而log是為了調(diào)試、后續(xù)debug的方便而添加的。良好的log,可以讓我們在log文件中,看清軟件的實際運行狀態(tài)。
在閱讀代碼時,
一些例外
注釋,這種語法的存在還是有必要的,只是大部分程序員其實根本用不到、或用了以后應(yīng)該馬上刪除。
API
例如,最應(yīng)該寫注釋的,是提供給別人——通常是另一個team、甚至公司——用的public接口,也就是所謂API。
Java的API文檔,往往是通過javadoc生成的。這樣的好處是,開發(fā)代碼的同時開發(fā)文檔,保持一致性。
不過通常不需要太多維護(hù),因為公開的API是不能隨意修改的,最大的改動往往是加一個@deprecated。
所以,其實API文檔分開單獨寫也不是不可以。
內(nèi)部用的public,代碼實現(xiàn)都能被看見,文檔也就可以免了。
實在是沒有文檔看不懂代碼,不會問嗎?
干“臟活兒”
有時候,需要表達(dá)的邏輯本身就很復(fù)雜。典型而又常見的就是多線程,往往難以理解和跟蹤。
很多時候,某些語言,尤其是C語言,干的都是些“臟活兒”。為了保證性能,往往必須怎么高效怎么來。一些遞歸函數(shù)里,多定義一個變量,都能影響到空間復(fù)雜度。
干臟活兒,想不臟,不容易。
不過,如果真的比較復(fù)雜,還是單獨的文檔,或者UML圖比較有效。
匯編就更不用提了。這個時代,你能寫,你不能指望別人能看。
TODO和FIXME
這倆,說是注釋,其實應(yīng)該算是標(biāo)記。
TODO: 表示還有未完成的工作,往往是開發(fā)過程中使用。
FIXME: 表示這里有bug,或者可能有bug,但暫時無法解決。這往往是發(fā)布時用。
不過,程序員的一大恨事就是:在別人release過來的代碼里,發(fā)現(xiàn)這哥倆!
臨時去掉一些代碼
調(diào)試時常用注釋來disable一些代碼,以免刪除了還得重寫或粘貼。
但是,調(diào)試完成后,不要的代碼一定要刪掉!
結(jié)論
- 趕緊刪注釋去!
- 不要主動寫API以外的注釋!
- 代碼要寫得好看一點。
- 以后的工作中,誰叫寫注釋就有理有據(jù)地噴回去!
- 你因看了本文而產(chǎn)生的任何實際變化,如果在工作和學(xué)習(xí)中帶來了任何負(fù)面結(jié)果,請自行負(fù)責(zé)。