<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          編寫(xiě)代碼注釋的最佳實(shí)踐

          共 9111字,需瀏覽 19分鐘

           ·

          2021-12-29 17:48

          ?點(diǎn)擊上方藍(lán)字關(guān)注我們,無(wú)用的注釋比沒(méi)有注釋更糟糕

          ?

          我們每天寫(xiě)代碼,做Code Review,努力提高代碼質(zhì)量。盡管已經(jīng)有很多資源可以幫助程序員們編寫(xiě)更好的代碼(例如書(shū)籍和靜態(tài)分析器),但很少有資源可以幫助編我們寫(xiě)出更好的注釋。

          衡量一個(gè)程序中的注釋數(shù)量很容易,衡量質(zhì)量卻很難。而且數(shù)量不代表質(zhì)量。無(wú)用的注釋比沒(méi)有注釋更糟糕。本文將為你帶來(lái)一些寫(xiě)出更好的注釋的最佳實(shí)踐。

          ?

          麻省理工學(xué)院著名教授哈爾·阿貝爾森 (Hal Abelson) 說(shuō):“程序應(yīng)該是為人們閱讀而編寫(xiě)的,只是偶然地供機(jī)器執(zhí)行”。

          雖然他說(shuō)的有點(diǎn)夸張,但他的這句話(huà)也闡明了程序有兩種截然不同的受眾:機(jī)器和人類(lèi)。編譯器和解釋器忽略注釋?zhuān)姓Z(yǔ)法正確的程序?qū)τ谒麄儊?lái)說(shuō)都是一樣容易理解。人類(lèi)則是非常不同的。我們很多時(shí)候我們會(huì)遇到一些難以理解的程序,我們希望通過(guò)注釋來(lái)幫助我們理解它們。

          糟糕的注釋比沒(méi)有注釋更糟糕。正如彼得沃格爾所寫(xiě)的:No Comment: Why Commenting Code Is Still a Bad Idea 有以下觀(guān)點(diǎn)

          1. 編寫(xiě)然后維護(hù)注釋將會(huì)花費(fèi)你的精力。

          2. 您的編譯器不會(huì)檢查您的注釋?zhuān)虼藷o(wú)法確定注釋是否正確。

          3. 另一方面,計(jì)算機(jī)完全按照您的代碼執(zhí)行的操作而不是注釋。

          雖然所有這些觀(guān)點(diǎn)都是正確的,但走到另一個(gè)極端并且從不寫(xiě)注釋是錯(cuò)誤的。雖然整潔的代碼會(huì)說(shuō)話(huà),但是在很復(fù)雜的代碼中可能說(shuō)的也不會(huì)那么清楚。

          這里有一些規(guī)則可以幫助您更好的寫(xiě)代碼注釋?zhuān)?/p>

          規(guī)則1:注釋不應(yīng)該重復(fù)代碼

          規(guī)則2:好的注釋也不應(yīng)該成為你糟糕的代碼的借口

          規(guī)則3:如果不能寫(xiě)出清晰的注釋?zhuān)赡苁悄愕拇a有問(wèn)題

          規(guī)則 4:注釋?xiě)?yīng)該消除混亂,而不是造成混亂

          規(guī)則 5:在注釋中解釋單語(yǔ)代碼

          規(guī)則 6:提供指向復(fù)制代碼原始來(lái)源的鏈接

          規(guī)則 7:在需要的地方包含指向外部參考的鏈接

          規(guī)則 8:在修復(fù)錯(cuò)誤時(shí)添加注釋

          規(guī)則 9:使用注釋來(lái)標(biāo)記不完整的實(shí)現(xiàn)

          下文將逐一解釋了這些規(guī)則,提供了示例并解釋了如何應(yīng)用它們。

          規(guī)則1:注釋不應(yīng)該重復(fù)代碼。

          許多初級(jí)程序員寫(xiě)了太多的注釋?zhuān)驗(yàn)樗麄冊(cè)谌腴T(mén)時(shí)被教導(dǎo)要多寫(xiě)注釋。甚至我見(jiàn)過(guò)高年級(jí)計(jì)算機(jī)科學(xué)課程的學(xué)生在每個(gè)封閉的大括號(hào)中添加注釋以指示哪個(gè)塊結(jié)束:

          if (x > 3) {
             …
          // if

          我還聽(tīng)說(shuō)過(guò)教師要求學(xué)生對(duì)每一行代碼進(jìn)行注釋。雖然這對(duì)于未入門(mén)的初學(xué)者來(lái)說(shuō)可能是一個(gè)合理的教導(dǎo),但這樣的注釋就像自行車(chē)的訓(xùn)練輪一樣,在學(xué)會(huì)后騎自行車(chē)時(shí)應(yīng)該去掉。

          沒(méi)有價(jià)值的注釋反而是會(huì)起負(fù)面作用的,因?yàn)樗鼈儯?/p>

          1. 讓你眼花繚亂

          2. 花費(fèi)你寶貴的時(shí)間來(lái)寫(xiě)和讀

          3. 改下代碼就可能會(huì)過(guò)時(shí)

          典型的壞例子是:

          i = i + 1// Add one to i

          它沒(méi)有添加任何有用信息,反而產(chǎn)生了維護(hù)成本。

          要求對(duì)每一行代碼進(jìn)行注釋的代碼。在 Reddit 上受到了十足的嘲笑:

          // create a for loop // <-- comment
          for // start for loop
          (   // round bracket
              // newline
          int // type for declaration
          i    // name for declaration
          =   // assignment operator for declaration
          0   // start value for i

          https://www.reddit.com/r/ProgrammerHumor/comments/5dhdt6/my_teacher_told_me_i_needed_to_comment_every_line/da56d6m

          規(guī)則2:好的注釋也不應(yīng)該成為你糟糕的代碼的借口

          注釋的另一個(gè)誤用是提供本應(yīng)包含在代碼中的信息。一個(gè)簡(jiǎn)單的例子是當(dāng)有人用一個(gè)字母命名一個(gè)變量,然后再添加一個(gè)描述其用途的注釋?zhuān)?/p>

          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;
          }

          這兒完全可以通過(guò)更好的變量命名來(lái)消除對(duì)注釋的需求:

          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 在《The Elements of Programming Style》 中所寫(xiě),“Don’t comment bad code — rewrite it.”

          規(guī)則3:如果不能寫(xiě)出清晰的注釋?zhuān)赡苁悄愕拇a有問(wèn)題。

          Unix 源代碼中最臭名昭著的注釋是“You are not expected to understand this” (你不需要理解這個(gè)),它出現(xiàn)在一些復(fù)雜的上下文切換代碼之前。丹尼斯·里奇 (Dennis Ritchie) 后來(lái)解釋說(shuō),它的目的是“本著‘這不會(huì)出現(xiàn)在考試中’的精神,而不是無(wú)禮的挑釁。”不幸的是,事實(shí)證明,他和合著者肯·湯普森自己并不理解,后來(lái)不得不重寫(xiě)。

          這讓人想起柯林漢定律:

          ?

          Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

          調(diào)試的難度是最初編寫(xiě)代碼的兩倍。因此,如果您盡可能聰明地編寫(xiě)代碼,根據(jù)定義,您就不夠聰明來(lái)調(diào)試它。

          (Brian Kernighan)

          ?

          https://github.com/dwmkerr/hacker-laws#kernighans-law

          警告讀者遠(yuǎn)離您的代碼就像打開(kāi)汽車(chē)的危險(xiǎn)信號(hào)燈:表明您正在做的事情是非法的。此時(shí)也許更好的是將代碼重寫(xiě)以便于您足夠理解的內(nèi)容,然后再留下明確的注釋。

          規(guī)則 4:注釋?xiě)?yīng)該消除混亂,而不是造成混亂

          如果沒(méi)有史蒂文·列維的《黑客:計(jì)算機(jī)革命的英雄》這個(gè)故事,對(duì)糟糕注釋的討論就不會(huì)完整:

          ?

          [Peter Samson] 拒絕在他的源代碼中添加注釋來(lái)解釋他在特定時(shí)間所做的事情,這一點(diǎn)尤其晦澀。Samson 編寫(xiě)的一個(gè)分布良好的程序繼續(xù)執(zhí)行數(shù)百條匯編語(yǔ)言指令,在包含數(shù)字 1750 的指令旁邊只有一個(gè)注釋。注釋是 RIPJSB,人們絞盡腦汁想它的含義,直到有人發(fā)現(xiàn) 1750 是巴赫去世的那一年,薩姆森寫(xiě)下了安息約翰·塞巴斯蒂安·巴赫(Rest In Peace Johann Sebastian Bach.)的縮寫(xiě)。

          ?

          雖然我們都很欣賞這樣厲害的黑客,但這不是一個(gè)好的示范。如果您的注釋引起混亂,請(qǐng)將其刪除。

          規(guī)則 5:在注釋中解釋非光慣用代碼。

          對(duì)其他人可能認(rèn)為不需要或多余的代碼進(jìn)行注釋是個(gè)好主意,例如來(lái)自 App Inventor 的代碼(我所有正面示例的來(lái)源):

          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;
          }

          如果沒(méi)有注釋?zhuān)腥丝赡軙?huì)“簡(jiǎn)化”代碼或?qū)⑵湟暈樯衩氐夭豢缮俚闹湔Z(yǔ)。通過(guò)寫(xiě)下為什么需要代碼來(lái)節(jié)省未來(lái)讀者的時(shí)間和焦慮。

          需要判斷代碼是否需要解釋。在學(xué)習(xí)Kotlin的時(shí)候,遇到過(guò)一個(gè)Android教程中的代碼,形式如下:

          if (b == true)

          我立即想知道是否可以替換為:

          if (b)

          就像在 Java 中所做的那樣。經(jīng)過(guò)一番研究,我了解到Nullable Boolean顯式地與 true 進(jìn)行比較,可以避免像下面這種丑陋的空檢查:

          if (b != null && b)

          建議不要包含對(duì)這種常見(jiàn)用法的注釋?zhuān)菍?zhuān)門(mén)為新手編寫(xiě)教程

          規(guī)則 6:提供指向復(fù)制代碼原始來(lái)源的鏈接。

          如果您像大多數(shù)程序員一樣,有時(shí)會(huì)使用在網(wǎng)上找到的代碼。包括對(duì)來(lái)源的引用使未來(lái)的讀者能夠獲得完整的上下文,例如:

          1. 正在解決什么問(wèn)題 

          2. 誰(shuí)提供了代碼

          3. 為什么推薦該解決方案 

          4. 注釋者是怎么想的

          5. 是否仍然有效

          6. 如何改進(jìn)

          例如,請(qǐng)考慮以下注釋

          /** Converts a Drawable to Bitmap. via https://stackoverflow.com/a/46018816/2219998. */

          按照答案的鏈接顯示:

          代碼的作者是 Tomá? Procházka,他是 Stack Overflow 上排名前 3% 的人。一位評(píng)論者提供了一個(gè)優(yōu)化,已經(jīng)合并到 repo 中。另一位評(píng)論者提出了一種避免邊緣情況的方法。將其與此注釋進(jìn)行對(duì)比(略有改動(dòng)以保護(hù)有罪者):

          // 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 比查找引用要快得多。

          一些程序員可能不愿意表明他們沒(méi)有自己編寫(xiě)代碼,但重用代碼可是一個(gè)明智的舉動(dòng),既節(jié)省時(shí)間而且廣經(jīng)測(cè)試。當(dāng)然,你永遠(yuǎn)不應(yīng)該粘貼你不理解的代碼。

          人們從 Stack Overflow 問(wèn)題和答案中復(fù)制了大量代碼。該代碼屬于需要署名的知識(shí)共享許可。參考注釋滿(mǎn)足該要求。

          同樣,您應(yīng)該參考有用的教程,以便可以再次找到它們并感謝他們的作者:

          // Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
          // for a great reference and examples.

          規(guī)則 7:在需要的地方包含指向外部參考的鏈接。

          當(dāng)然,并非所有引用都指向 Stack Overflow??紤]:

          // http://tools.ietf.org/html/rfc4180 suggests that CSV lines
          // should be terminated by CRLF, hence the \r\n.
          csvStringBuilder.append("\r\n");

          標(biāo)準(zhǔn)和其他文檔的鏈接可以幫助讀者了解您的代碼正在解決的問(wèn)題。盡管此信息可能位于設(shè)計(jì)文檔中的某個(gè)位置,但位置恰當(dāng)?shù)淖⑨尶蔀樽x者提供最需要的時(shí)間和地點(diǎn)的指示。

          規(guī)則 8:在修復(fù)錯(cuò)誤時(shí)添加注釋。

          不僅應(yīng)該在最初編寫(xiě)代碼時(shí)添加注釋?zhuān)€應(yīng)該在修改代碼時(shí)添加注釋?zhuān)绕涫窃谛迯?fù)錯(cuò)誤時(shí)??紤]這個(gè)注釋?zhuān)?/p>

          // 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)前和引用方法中的代碼,還有助于確定代碼是否仍然需要以及如何對(duì)其進(jìn)行測(cè)試。

          引用問(wèn)題跟蹤器也是很有幫助的:

          // Use the name as the title if the properties did not include one (issue #1425)

          雖然 git blame 可用于查找添加或修改行的提交,但提交消息往往很簡(jiǎn)短,并且最重要的更改(例如,修復(fù)問(wèn)題 #1425)可能不是最近提交的一部分(例如,將方法從一個(gè)文件移動(dòng)到另一個(gè)文件)

          規(guī)則 9:使用注釋來(lái)標(biāo)記未完成的實(shí)現(xiàn)。

          有時(shí)即使代碼有已知的缺陷,也有必要發(fā)布。雖然不提示代碼中已知的缺陷可能很誘人,但最好使這些缺陷明確,例如使用 TODO 注釋?zhuān)?/p>

          // 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ì)此類(lèi)注釋使用標(biāo)準(zhǔn)格式有助于衡量和解決技術(shù)債務(wù)。更好的是,向您的跟蹤器添加一個(gè)問(wèn)題,并在您的注釋中引用該問(wèn)題。

          結(jié)論

          我希望上面的這些例子已經(jīng)表明注釋不會(huì)成為糟糕代碼的借口;它們通過(guò)提供不同類(lèi)型的信息來(lái)補(bǔ)充良好的代碼。正如 Stack Overflow 聯(lián)合創(chuàng)始人 Jeff Atwood 所寫(xiě),Code Tells You How, Comments Tell You Why (代碼告訴你如何,注釋告訴你為什么)

          良好的遵循這些規(guī)則應(yīng)該可以節(jié)省您和您的隊(duì)友的時(shí)間。

          我確信這些規(guī)則并非詳盡無(wú)遺,并期待在注釋中看到建議的補(bǔ)充。

          本文完。
          ?

          親愛(ài)的讀者,

          感謝你的時(shí)間。我們下期再見(jiàn)!

          如果你在評(píng)論區(qū)留下的想法,我會(huì)十分高興。

          ?

          往期精彩回顧:

          懶惰開(kāi)發(fā)者需要知道 React Hack
          如何寫(xiě)好 commit message
          Web 開(kāi)發(fā)者需要知道的 18 個(gè)殺手級(jí)網(wǎng)站
          一篇文章,教你學(xué)會(huì)Git
          成為現(xiàn)代 JavaScript 大師的 3 個(gè)小技巧
          8個(gè)必知的 SEO 優(yōu)化重要元標(biāo)簽

          關(guān)注公眾號(hào)回復(fù)【資源】可免費(fèi)領(lǐng)取學(xué)習(xí)資料!


          左手代碼右手磚,拋磚引玉

          記得點(diǎn)贊,分享,在看加關(guān)注喲

          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情啪啪啪网站 | 成年人精品 | 色情视频网站在线免费观看 | 青青操人人操 | www国产夜插内射视频网站 |