註解能在不同細節層次上補強程式碼:
| 註解層次 | 提供的價值 |
|---|---|
| 比程式碼更底層(更精確) | 釐清精確含義 |
| 比程式碼更高層(更抽象) | 提供直覺、原因、簡化視角 |
| 與程式碼同層次 | 多半在重複程式碼 |
何時用底層註解#
「底層註解 / 提高精確度」最適合用在變數宣告:類別實例變數、方法參數、回傳值。
變數的名稱與型別本身通常不夠精確。註解可填補:
- 這個變數的單位?
- 邊界條件是包含還是排除(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;- 變數名也改了(
lineWidths→numLinesWithLength),與註解配合 - 把「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」。