寫代碼時,我們常常聽到“要多寫註釋”的建議。但你可曾想過,有些註釋比沒有註釋更糟糕?它們不僅無法幫助理解代碼,反而會成為團隊的負擔。
1. “TODO”註釋:永遠的明日清單
def calculate_salary(employee):
# TODO: 這裏需要優化性能
result = 0
for i in range(1000000):
result += complex_calculation(employee)
return result
這種註釋最大的問題是,它往往變成了“永遠不做”的代名詞。我見過一個代碼庫裏有200多個TODO註釋,最早的可以追溯到5年前。
更好的做法:
- 如果是重要任務,創建正式的工單
- 如果是小優化,立即動手完成
- 定期清理代碼庫中的TODO
2. “魔法數字”的“解釋”
public class OrderProcessor {
public void processOrder(Order order) {
if (order.getItems().size() > 99) { // 99是系統限制的最大項目數
throw new OrderTooLargeException();
}
}
}
註釋解釋了數字的含義,但更好的方式是讓代碼自我解釋。
改進方案:
public class OrderProcessor {
private static final int MAX_ORDER_ITEMS = 99;
public void processOrder(Order order) {
if (order.getItems().size() > MAX_ORDER_ITEMS) {
throw new OrderTooLargeException();
}
}
}
3. 過時的“歷史記錄”
/**
* 修改記錄:
* 2018-03-12 張三:修復了登錄問題
* 2019-07-22 李四:添加了記住密碼功能
* 2020-11-05 王五:重構了驗證邏輯
* 2021-05-18 趙六:又修復了登錄問題
*/
function validateUser(username, password) {
// 當前實現
}
這類註釋會迅速過時,而且Git能更好地記錄歷史。
4. 廢話註釋
def add(a, b):
"""
這個函數用來相加兩個數字
參數:
a: 第一個數字
b: 第二個數字
返回:
兩個數字的和
"""
return a + b
代碼本身已經清楚地表達了功能,這種註釋純粹是噪音。
5. 發泄情緒的註釋
// 這坨垃圾代碼是前任寫的,我也不知道為什麼這麼寫
// 但改了會出問題,所以別動它!!!
void legacyFunction() {
// 令人困惑的實現
}
消極情緒會傳染,而且可能會讓後來的開發者不敢修改這段代碼,即使它確實需要重構。
6. 過於詳細的註釋
public void SaveUser(User user)
{
// 步驟1: 檢查用户對象是否為null
if (user == null)
{
// 如果為null,拋出ArgumentNullException異常
throw new ArgumentNullException(nameof(user));
}
// 步驟2: 驗證用户數據
// 這裏我們檢查用户名是否為空
if (string.IsNullOrEmpty(user.Username))
{
// 如果用户名為空,拋出ArgumentException異常
throw new ArgumentException("用户名不能為空", nameof(user.Username));
}
// 步驟3: 保存到數據庫
// 調用數據庫上下文的SaveChanges方法
_dbContext.SaveChanges();
// 步驟4: 記錄日誌
// 使用ILogger接口記錄信息級別日誌
_logger.LogInformation("用户已保存");
}
好的代碼應該像故事一樣可讀,不需要逐行解説。
7. 誤導性註釋
// 這個函數會返回用户的全名
function getUserEmail(userId: number): string {
// 實際返回的是郵箱,不是全名
return userDatabase.getEmailById(userId);
}
這種註釋比沒有註釋更糟糕,因為它提供了錯誤的信息。
8. “這裏很複雜”的註釋
// 這裏的邏輯很複雜,涉及多個系統交互
// 修改前請務必仔細測試
public Result processTransaction(Transaction transaction) {
// 複雜的業務邏輯
}
與其警告別人這裏很複雜,不如:
- 拆分成小函數
- 添加有意義的函數和變量名
- 編寫單元測試作為文檔
9. 被註釋掉的代碼
function calculatePrice(quantity, price) {
// let total = quantity * price;
// if (total > 1000) {
// total = total * 0.9; // 舊折扣邏輯
// }
let total = quantity * price * getDiscountRate();
return total;
}
被註釋掉的代碼會讓讀者困惑:這是不是未來要恢復的功能?為什麼不刪除?
10. 自誇式註釋
# 這個算法是我獨創的,比傳統方法快10倍
# 曾獲得公司創新獎,請不要隨意修改
def superFastAlgorithm(data):
# 實現細節
pass
這種註釋創造了一種“個人領地”感,不利於團隊協作和代碼改進。
如何寫出好註釋?
好的註釋應該:
- 解釋“為什麼”而不是“是什麼”
# 使用快速排序而不是內置排序,因為我們的數據幾乎已排序
def sort_data(data):
return quick_sort(data)
- 記錄重要的設計決策
// 選擇Redis而不是數據庫緩存,因為我們需要亞毫秒級響應
// 見設計文檔:鏈接
public class CacheService {
// 實現
}
- 提醒可能的陷阱
// 注意:這個API有速率限制,每秒最多10次調用
// 如果超限會返回429狀態碼
async function callExternalAPI(data) {
// 實現
}
- 使用清晰的API文檔
/**
* 計算兩個地理位置之間的直線距離
* @param lat1 第一個點的緯度
* @param lon1 第一個點的經度
* @param lat2 第二個點的緯度
* @param lon2 第二個點的經度
* @returns 距離(公里)
* @throws 當座標無效時拋出RangeError
*/
function calculateDistance(lat1: number, lon1: number,
lat2: number, lon2: number): number {
// 實現
}
註釋的黃金法則
- 如果代碼需要註釋來解釋它在做什麼,先嚐試重構代碼
- 註釋應該解釋代碼無法表達的信息
- 把註釋當作給6個月後的自己看的備忘
- 定期審查和更新註釋
結語
代碼註釋就像調料,適量能提升風味,過量則破壞口感。最好的代碼是自解釋的代碼,但當我們確實需要註釋時,應該確保它們提供了真正的價值。
下次寫註釋前,不妨問問自己:這個註釋6個月後還有用嗎?它會幫助同事理解我的代碼嗎?如果答案是否定的,也許你根本不需要這個註釋。