作者 | Ellen Spertus 譯者 | 王雪迎 雖然有很多資源可以幫助程序員編寫更好的代碼,比如書籍或靜態(tài)分析器,但是很少有資源可被用于編寫更好的注釋。雖然度量一個(gè)程序中的注釋數(shù)量很容易,但衡量注釋的質(zhì)量卻很難,而且兩者并不一定相關(guān)。差的注釋比根本不寫注釋更糟糕。這里有一些規(guī)則可以幫助你實(shí)現(xiàn)一種折中的方法。 麻省理工學(xué)院的著名教授Hal Abelson曾說過:“程序必須寫給人們閱讀,而只是附帶地讓機(jī)器執(zhí)行?!彪m然他可能故意低估了運(yùn)行代碼的重要性,但卻注意到了程序有兩種截然不同的受眾。編譯器和解釋器會(huì)忽略注釋,找出同等容易理解的所有語法正確的程序。而人類讀者則完全不同。我們發(fā)現(xiàn)有些程序比其它的更難理解,此時(shí)就會(huì)通過查看注釋來幫助我們理解這些程序。 雖然有很多資源可以幫助程序員編寫更好的代碼,比如書籍或靜態(tài)分析器,但是很少有資源可被用于編寫更好的注釋。雖然度量一個(gè)程序中的注釋數(shù)量很容易,但衡量注釋的質(zhì)量卻很難,而且兩者并不一定相關(guān)。差的注釋比根本不寫注釋更糟糕。正如Peter Vogel所述:
所有這些觀點(diǎn)都是正確的,但如果走到另一個(gè)極端,即從不寫注釋,那將是一個(gè)錯(cuò)誤。這里有一些規(guī)則可以幫助你實(shí)現(xiàn)一種折中的方法:
本文其余部分將逐條解釋這些規(guī)則,提供示例并說明如何以及何時(shí)應(yīng)用它們。 01 規(guī)則1:注釋不應(yīng)與代碼重復(fù) 許多初級(jí)程序員會(huì)寫太多注釋,因?yàn)樗麄兘邮芰巳腴T指導(dǎo)老師的培訓(xùn)。我曾見過計(jì)算機(jī)科學(xué)系的高年級(jí)學(xué)生為每對(duì)大括號(hào)加上一條注釋,以表示該塊結(jié)束: if (x > 3) { … } // if 我也聽說過老師要求學(xué)生對(duì)每一行代碼進(jìn)行注釋。雖然這對(duì)極為初級(jí)者來說可能是一個(gè)合理的策略,但這樣的注釋就像是訓(xùn)練輪,在大孩子騎車時(shí)應(yīng)該去掉。 不添加任何信息的注釋具有負(fù)價(jià)值,因?yàn)樗鼈儯?/p>
一個(gè)典型的壞示例為: i = i + 1; // Add one to i 它不添加任何信息,并切產(chǎn)生維護(hù)成本。 每一行代碼都需要注釋的策略在Reddit上都受到了相當(dāng)?shù)某靶Γ?/p> 02 規(guī)則2:好的注釋不能成為代碼不清晰的借口 注釋的另一個(gè)誤用是提供本應(yīng)包含在代碼中的信息。一個(gè)簡(jiǎn)單的例子是,有人用一個(gè)字母命名一個(gè)變量,然后添加一個(gè)描述其用途的注釋: private static Node getBestChildNode(Node node) { Node n; // best child node candidate for (Node node: node.getChildren()) { // update n if the current state is better if (n == null || utility(node) > utility(n)) { n = node; } } return n;} 通過更好的變量命名,可以不需要再做注釋: private static Node getBestChildNode(Node node) { Node bestNode; for (Node currentNode: node.getChildren()) { if (bestNode == null || utility(currentNode) > utility(bestNode)) { bestNode = currentNode; } } return bestNode; } 正如Kernighan和Plauger在編程風(fēng)格要素一書中所寫,“不要注釋糟糕的代碼 — 重寫它?!?/p> 03 規(guī)則3:如果無法寫出一個(gè)清晰的注釋,代碼可能有問題 Unix源代碼中最臭名昭著的注釋是“你不希望理解它”,它出現(xiàn)在一些恐怖的上下文切換代碼之前。Dennis Ritchie后來解釋說這是故意為之:“本意是想表達(dá)'這不會(huì)出現(xiàn)在考試中’,而不是作為一種厚顏無恥的挑戰(zhàn)。”不幸的是,結(jié)果發(fā)現(xiàn)他與合作者Ken Thompson自己也理解不了,后來不得不重寫。 這讓人想起了Kernighan定律: 調(diào)試在一開始就比編寫程序困難一倍。因此,按照定義,如果你的代碼寫得非常巧妙,那么你就沒有足夠的能力來調(diào)試它。 警告讀者遠(yuǎn)離你的代碼就像打開汽車的危險(xiǎn)警示燈:承認(rèn)你正在做知法犯法的事情。相反,把代碼重寫成你充分理解的東西,或者更進(jìn)一步,直截了當(dāng)?shù)剡M(jìn)行解釋。 04 規(guī)則4:注釋應(yīng)該消除混亂,而不是引起混亂 如果沒有Steven Levy的 黑客:計(jì)算機(jī)革命的英雄 中的這個(gè)故事,任何關(guān)于負(fù)面注釋的討論都是不完整的: [Peter Samson]拒絕在源代碼中添加注釋來解釋他在特定時(shí)間所做的事情,使其特別晦澀難懂。在一個(gè)分發(fā)良好的程序中,Samson繼續(xù)編寫了數(shù)百條匯編語言指令,其中只有一條包含1750數(shù)字的指令帶有注釋。注釋是RIPJSB,人們絞盡腦汁研究它的含義,直到有人發(fā)現(xiàn)1750年是巴赫去世的那一年,而Samson寫的注釋是Rest In Peace Johann Sebastian Bach(安息吧,約翰·塞巴斯蒂安·巴赫)的縮寫。 雖然我和別人一樣欣賞一個(gè)好的黑客,但這種注釋不可效仿。如果你的注釋引起混亂而不是消除混亂,刪除它。 05 規(guī)則5:在注釋中解釋不規(guī)范的代碼 注釋掉其他人可能認(rèn)為不需要或冗余的代碼是個(gè)好主意,比如來自App Inventor的代碼(我所有的正面示例均源于此): final Object value = (new JSONTokener(jsonString)).nextValue();// Note that JSONTokener.nextValue() may return// a value equals() to null.if (value == null || value.equals(null)) { return null;} 如果沒有注釋,有人可能會(huì)“簡(jiǎn)化”代碼或?qū)⑵湟暈橐粋€(gè)神秘但必不可少的咒語。寫下為什么需要這些代碼,可以節(jié)省未來讀者的時(shí)間并解除他們的焦慮。 需要對(duì)是否注釋代碼做出判斷。在學(xué)習(xí)Kotlin時(shí),我遇到了Android教程中的代碼: if (b == true) 我立即想到是否可以用以下代碼取代: if (b) 就像在Java中一樣。經(jīng)過一點(diǎn)研究,我了解到可以為null的布爾變量會(huì)顯式地與true進(jìn)行比較,以避免出現(xiàn)難看的null檢查: if (b != null && b) 我建議不要為常見的語法添加注釋,除非專門為新手編寫教程。 06 規(guī)則6:提供復(fù)制代碼的原始出處鏈接 如果你像大多數(shù)程序員一樣,有時(shí)會(huì)使用網(wǎng)上找到的代碼。包含對(duì)源代碼的引用,可使將來的讀者能夠獲得完整的上下文,例如:
例如,考慮此注釋: /** Converts a Drawable to Bitmap. via https:///a/46018816/2219998. */ 點(diǎn)開鏈接,給出了以下信息:
將其與下面的注釋對(duì)比(稍作修改以防止侵權(quán)): // Magical formula taken from a stackoverflow post, reputedly related to // human vision perception. return (int) (0.3 * red + 0.59 * green + 0.11 * blue); 任何想要理解這段代碼的人都必須搜索公式。粘貼到URL要比以后查找引用快得多。 有些程序員可能不愿意表明他們自己并沒有編寫代碼,但是重用代碼是一個(gè)明智的舉動(dòng),它可以節(jié)省時(shí)間并讓你獲得更多關(guān)注。當(dāng)然,永遠(yuǎn)不要粘貼不懂的代碼。 人們從Stack Overflow的問題和答案中復(fù)制大量代碼。這些代碼要求歸屬于知識(shí)共享許可下。引用注釋滿足該要求。 同樣,你應(yīng)該引用一些有用的教程以便再次找到它們,并且感謝它們的作者: // Many thanks to Chris Veness at http://www./scripts/latlong.html// for a great reference and examples. 07 規(guī)則7:在可能提供幫助的地方引入指向外部參考的鏈接 當(dāng)然,并非所有引用都指向Stack Overflow??紤]: // http://tools./html/rfc4180 suggests that CSV lines // should be terminated by CRLF, hence the \r\n. csvStringBuilder.append('\r\n'); 到標(biāo)準(zhǔn)或其它文檔的鏈接可以幫助讀者理解代碼正在解決的問題。雖然這些信息可能出現(xiàn)在設(shè)計(jì)文檔中的某個(gè)地方,但一個(gè)適當(dāng)位置的注釋會(huì)在最需要它的時(shí)間和地方為讀者提供標(biāo)識(shí)。本例中,點(diǎn)開鏈接可以看到RFC 4180已經(jīng)由RFC 7111更新了有用的信息。 08 規(guī)則8:在修復(fù)bug時(shí)添加注釋 不僅應(yīng)該在最初編寫代碼時(shí)添加注釋,修改代碼時(shí),特別是修復(fù)bug時(shí),也應(yīng)該添加。看下面這個(gè)注釋: // NOTE: At least in Firefox 2, if the user drags outside of the browser window, // mouse-move (and even mouse-down) events will not be received until // the user drags back inside the window. A workaround for this issue // exists in the implementation for onMouseLeave(). @Override public void onMouseMove(Widget sender, int x, int y) { .. } 注釋不僅有助于讀者理解當(dāng)前方法和引用方法中的代碼,而且有助于確定是否仍然需要這些代碼以及如何測(cè)試它們。 注釋還可以幫助參考問題跟蹤程序: // Use the name as the title if the properties did not include one (issue #1425) 雖然 git blame 可以用來查找添加或修改行的提交,但提交消息往往很簡(jiǎn)短,最重要的更改(例如,修復(fù)問題#1425)可能不是最近提交(例如,將方法從一個(gè)文件移動(dòng)到另一個(gè)文件)的一部分。 09 規(guī)則9:使用注釋來標(biāo)記未完成的實(shí)現(xiàn) 即使代碼中有已知的限制,有時(shí)還是有必要檢查它。雖然不分享代碼中已知的缺陷很有誘惑力,但最好將這些明確化,例如使用TODO注釋: // TODO(hal): We are making the decimal separator be a period,// regardless of the locale of the phone. We need to think about// how to allow comma as decimal separator, which will require// updating number parsing and other places that transform numbers// to strings, such as FormatAsDecimal 對(duì)此類注釋使用標(biāo)準(zhǔn)格式有助于衡量和解決技術(shù)負(fù)債問題。更好的辦法是,在跟蹤系統(tǒng)中添加一個(gè)問題,并在注釋中引用該問題。 10 總結(jié) 我希望上面的例子已經(jīng)表明,注釋不能成為差代碼的借口或修復(fù)差代碼;它們通過提供不同類型的信息來補(bǔ)充好的代碼。正如Stack Overflow聯(lián)合創(chuàng)始人Jeff Atwood所寫,“代碼告訴你怎么做,注釋告訴你為什么?!?/p> 遵守這些規(guī)則可以節(jié)省你及其隊(duì)友的時(shí)間,減少挫折感。 即便如此,我確信這些規(guī)則并不是詳盡無遺的,并期待著在評(píng)論中看到補(bǔ)充建議。 參考資料:
原文鏈接:https://stackoverflow.blog/2021/07/05/best-practices-for-writing-code-comments/ 聲明:本文由CSDN翻譯,轉(zhuǎn)載請(qǐng)注明來源。 -End- 最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載! |
|