[譯] 程式碼中新增註釋之好壞醜

Bamboo發表於2017-04-27

程式碼中新增註釋之好壞醜

[譯] 程式碼中新增註釋之好壞醜

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

如果你以前聽過這句話就打斷我...

「好的程式碼自身就是文件」。

在我 20 多年以寫程式碼為生的經歷中,這是我聽得最多的一句話。

陳詞濫調

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

這句話正確嗎?是的

那是不是意味著你不應該給你的程式碼寫註釋?不是

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

初學者需要了解,實際上有兩種不同型別的程式碼註釋,我稱之為文件註釋說明性註釋

文件註釋

文件註釋是為了給任何可能使用你的原始碼的人看的,但他們不一定會通讀程式碼。如果你正在構建給其他開發者使用的庫或框架,你需要某種形式的 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前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章