[譯] 代碼中添加註釋之好壞醜

代碼中添加註釋之好壞醜

題圖是克林特 · 伊斯特伍德在《黃金三鏢客》中劇照。javascript

若是你之前聽過這句話就打斷我...html

「好的代碼自身就是文檔」。前端

在我 20 多年以寫代碼爲生的經歷中,這是我聽得最多的一句話。java

陳詞濫調react

像其餘許多陳詞濫調同樣,它的核心是一個真理。可是這個真理已經被濫用,大多數說出這句話的人並不知道它的真正意思。android

這句話正確嗎?是的ios

那是否是意味着你不該該給你的代碼寫註釋?不是git

本文中,咱們將介紹一下給代碼寫註釋的好處、壞處和醜處。程序員

初學者須要瞭解,實際上有兩種不一樣類型的代碼註釋,我稱之爲文檔註釋說明性註釋github

文檔註釋

文檔註釋是爲了給任何可能使用你的源代碼的人看的,但他們不必定會通讀代碼。若是你正在構建給其餘開發者使用的庫或框架,你須要某種形式的 API 文檔。

越早從源代碼中提取 API 文檔,隨着時間的推移,文檔就越有可能變得過期或不許確。減小這種狀況的一個好策略就是直接將文檔嵌入代碼中,以後再使用工具提取文檔。

下面是一個文檔註釋的例子,來自一個流行的 JavaScript 庫,叫作 Lodash

/** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The corresponding value of * each key is the number of times the key was returned by `iteratee`. The * iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 0.5.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([6.1, 4.2, 6.3], Math.floor); * // => { '4': 1, '6': 2 } * * // The `_.property` iteratee shorthand. * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */
    var countBy = createAggregator(function(result, value, key) {
      if (hasOwnProperty.call(result, key)) {
        ++result[key];
      } else {
        baseAssignValue(result, key, 1);
      }
    });複製代碼

若是你將這些註釋與他們的線上文檔作對比,你會發現它們徹底一致。

若是你開始使用文檔註釋,則須要確保這些註釋遵循一致的標準,而且使它們與其餘說明性的註釋能夠輕易區分開。一些普遍使用、有良好支持的標準和工具包括 JavaScript 的 JSDoc,dotNet 的 DocFx,Java 的 JavaDoc

這種註釋的缺點就是使你的代碼很是「嘈雜」,並使得積極參與維護的人更難閱讀代碼。好消息是,大可能是代碼編輯器都支持「代碼摺疊」的功能,這樣就能夠摺疊這部分註釋,專一在代碼上。

上圖演示在 Visual Studio Code 中摺疊註釋。

說明性註釋

說明性註釋是給任何可能須要維護、重構或擴展你的代碼的人(包括你本身)看的。

一般來講,須要說明性註釋的代碼散發着一種壞代碼的氣味,它的出現說明你的代碼太複雜了。你應該儘可能簡化代碼並刪除這種註釋,由於「好的代碼自身就是文檔」。

如下是一個很差的 —— 雖然頗有趣 —— 說明性註釋的例子

/* * Replaces with spaces * the braces in cases * where braces in places * cause stasis. * (將大括號替換爲空格,若是大括號形成停滯) **/ 
$str = str_replace(array("\{","\}")," ",$str);複製代碼

若是做者不花時間在使用韻腳詩裝點這個稍微使人疑惑的代碼,確定能夠將代碼自己寫的更加易讀易懂。也許命名一個函數,removeCurlyBraces 在另外一個函數 sanitizeInput 中調用?

不要會錯意,的確有很多時候 —— 特別當你正在拼命應對繁重的工做時 —— 注入一些幽默對身心都有好處。可是當你寫了一個有趣的註釋來修飾很差的代碼時,實際上人們不太可能稍後重構或修復代碼。

你真的想爲掠奪全部將來程序員閱讀這首聰明的押韻詩的樂趣而負責嗎?大多數的程序員會笑起來,而忽略了這段代碼自己的問題。

你也會遇到多餘的註釋。若是代碼已經足夠簡單明瞭,就不須要再添加註釋了。

好比說,不要作下面這種毫無心義的事:

/* 將年齡的整數值設爲 32 */
int age = 32;複製代碼

不過,有時候,不管你對代碼自己作了什麼,一個說明性註釋仍是須要的。

這一般發生在你須要添加一些上下文解釋一個不太直觀的解決方法。

如下是一個來自 Lodash 的很好的例子:

function addSetEntry(set, value) {   
  /* 不要返回 `set.add`,由於它在 IE 11 中不可連接。 */  
  set.add(value);    
  return set;  
}複製代碼

也有一些狀況是 ,在通過不少思考和實驗後 ,看上去天真的解決方法事實上是最好的。在這些狀況下,其餘的程序員會不可避免地認爲他們更聰明並開始本身動手實踐,最後卻發現你的方法是最好的。

有時上面提到的其餘程序員就是將來的你。

在這些狀況下,最好的作法就是寫下注釋,節省全部人的時間,避免尷尬。

如下這個註釋完美地詮釋了這種狀況:

/** 親愛的維護者: 當你完成了嘗試「優化」這部分代碼, 並意識到這是個多麼大的錯誤時, 請增長下面的計數器以給下一我的警告: 總共在此處浪費的小時數 = 42 **/複製代碼

固然,上面的例子更可能是有趣,而不是有幫助。可是你應該留下注釋,警告其餘人不要追求一些看似明顯的「更好的解決方法」,由於你已經嘗試過並否決了。當你這樣作的時候,應該明確指出你嘗試了哪些方案,爲何否決了這些方案。

如下是一個 JavaScript 中的簡單例子:

/* 不要使用全局 isFinite(),由於它對 null 值會返回 true. */
Number.isFinite(value)複製代碼

醜處

咱們介紹了好處、壞處,那麼醜處呢?

有時候你會感到沮喪,特別當你爲謀生而寫代碼時,在代碼註釋中你傾向於將這種沮喪的情緒發泄出來。

使用過不少的代碼庫後,你會見到各類各樣憤世嫉俗、沮喪到黑暗或意味深長的註釋。

像這種看似無害的註釋...

/* 這段代碼很糟糕,你知道,我也知道。 繼續往前,以後再叫我白癡。 */複製代碼

...以及這種直白刻薄的註釋

/* 這是配合 Richard 的工做而寫的類,他是個白癡 */複製代碼

這些可能看起來頗有趣,或者在當時幫助你發泄了一部分情緒。可是當你把它們變成生產環境代碼時,它們使得編寫它們的程序員以及他們的僱主看起來不專業和苦大仇深似的。

不要這麼作。

若是你喜歡這篇文章,請在這篇文章下方戳一下 ❤ 點個贊,幫助我傳播。若是你想閱讀更多其餘的文章,歡迎登記訂閱我每週的 Dev Mastery 簡報。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索