註解能在不同細節層次上補強程式碼:

註解層次提供的價值
比程式碼更底層(更精確)釐清精確含義
比程式碼更高層(更抽象)提供直覺、原因、簡化視角
與程式碼同層次多半在重複程式碼

何時用底層註解#

「底層註解 / 提高精確度」最適合用在變數宣告:類別實例變數、方法參數、回傳值。

變數的名稱與型別本身通常不夠精確。註解可填補:

  • 這個變數的單位
  • 邊界條件是包含還是排除(inclusive / exclusive)?
  • 允許 null 嗎?null 代表什麼?
  • 若變數參考某個資源(必須最後 free 或 close),誰負責釋放?
  • 是否有 invariants?(例如「這個 list 永遠至少有一個元素」)

「程式碼」指的是註解旁的那段#

這些資訊有些理論上可以從變數的所有使用處推得,但這既費時又易錯。

「註解應描述程式碼看不出的事」中,「程式碼」指註解旁的程式碼,不是「整個應用的所有程式碼」。

反例:太籠統#

// Current offset in resp Buffer
uint32_t offset;

// Contains all line-widths inside the document and
// number of appearances.
private TreeMap<Integer, Integer> lineWidths;

問題:

  • 「current」是什麼意思?
  • TreeMap 的 key 是 line widths 還是 occurrence counts?單位是字元還是像素?

改寫#

// Position in this buffer of the first object that hasn't
// been returned to the client.
uint32_t offset;

// Holds statistics about line lengths of the form <length, count>
// where length is the number of characters in a line (including
// the newline), and count is the number of lines with
// exactly that many characters. If there are no lines with
// a particular length, then there is no entry for that length.
private TreeMap<Integer, Integer> numLinesWithLength;
  • 變數名也改了(lineWidthsnumLinesWithLength),與註解配合
  • 把「width」換成「length」減少「像素」的歧義
  • 不僅描述每筆 entry,還說明 entry 不存在時的意義

想名詞,不要想動詞#

描述變數時,聚焦於它代表什麼,而不是怎麼被操作

反例:描述「怎麼被改」#

/* FOLLOWER VARIABLE: indicator variable that allows the Receiver
 * and the PeriodicTasks thread to communicate about whether a heartbeat
 * has been received within the follower's election timeout window.
 * Toggled to TRUE when a valid heartbeat is received.
 * Toggled to FALSE when the election timeout window is reset.
 */
private boolean receivedValidHeartbeat;

這描述的是「程式碼怎麼操作這個變數」,鏡射了程式結構。

改寫:描述「代表什麼」#

/* True means that a heartbeat has been received since the last time
 * the election timer was reset. Used for communication between the
 * Receiver and PeriodicTasks threads.
 */
private boolean receivedValidHeartbeat;

更短、更有用——讀者自然能推導「收到 heartbeat 時要設 true、reset 時要設 false」。