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