第6章:注释策略(Commenting Strategy)
一、导读
1.1 本章学习目标
第6章讲解注释的设计——什么应该注释,什么不应该注释。
本章的核心学习目标包括:
- 理解注释的目的
- 掌握接口注释和实现注释的区别
- 学会写有价值的注释
- 理解"代码即文档"的局限
1.2 为什么本章如此重要
好的注释能大幅降低理解代码的成本。坏的注释比没有注释更糟——它们会误导人。Ousterhout 的核心观点是:注释应该描述代码不能表达的信息。
1.3 核心问题引导
注释的目的是什么?
接口注释应该包含什么?
实现注释应该包含什么?
什么时候不需要注释?
二、核心概念详解
2.1 注释的目的
注释的目的:
降低理解代码的成本
注释应该描述:
代码本身无法表达的信息
注释不应该描述:
代码已经清楚表达的信息
// 坏注释:重复代码
int count = 0; // 设置 count 为 0
// 好注释:解释"为什么"
int count = 0;
// 从 0 开始,因为空集合的计数应该是 0,
// 而不是 -1 或其他值2.2 接口注释
接口注释:描述用户需要知道的信息
应该包含:
1. 方法做什么(不是如何做)
2. 参数的含义和约束
3. 返回值的含义
4. 副作用
5. 异常条件
// 好的接口注释
/**
* Transfers money between two accounts.
*
* @param from Source account (must belong to current user)
* @param to Destination account (must be active)
* @param amount Amount to transfer (must be positive)
* @throws InsufficientFundsException if from account has insufficient balance
* @throws AccountFrozenException if either account is frozen
*/
public void transfer(Account from, Account to, Money amount);
不应该包含:
- 实现细节
- 内部数据结构
- 算法步骤2.3 实现注释
实现注释:帮助理解代码如何工作
应该包含:
1. 为什么这样做(设计决策)
2. 不明显的约束
3. 警告和陷阱
4. 复杂算法的概述
// 好的实现注释
// 使用 Boyer-Moore 算法而不是 KMP,因为
// 在英文文本上 Boyer-Moore 平均快 3 倍
// (参见 Knuth, TAOCP Vol 3, Section 3.6)
// 这个魔法数字来自 IEEE 754 标准
// 不要修改,除非你完全理解浮点数表示
private static final long MAGIC = 0x5f3759dfL;
不应该包含:
- 显而易见的信息
- 过时的信息
- 被注释掉的代码2.4 不需要注释的情况
代码能表达的信息不需要注释:
// 不需要注释
int maxRetries = 3; // 最大重试次数
// 变量名已经说明了
// 不需要注释
if (user.isActive()) {
// 如果用户活跃
processUser(user);
}
// 代码已经自解释
// 不需要注释
public class Money {
private final BigDecimal amount;
private final Currency currency;
}
// 字段名已经说明了
原则:
好的命名胜过好的注释
如果代码需要注释才能理解,先考虑能否改善命名三、实战案例
3.1 案例一:改善注释
java
// 改善前:注释不好
/**
* Process the order.
* 什么都没说明
*/
public void process(Order order) {
// Check if order is valid
if (order.getItems().isEmpty()) {
throw new Exception("Invalid order"); // 没有说明异常
}
// Calculate total
double total = 0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity(); // 没说明折扣
}
// Save order // 显而易见的注释
repo.save(order);
}
// 改善后:注释有价值
/**
* Confirms and processes an order.
*
* Validates that the order has at least one item,
* calculates the total (including applicable discounts),
* and persists the order.
*
* @throws EmptyOrderException if order has no items
* @throws PriceChangedException if prices changed since order was created
*/
public void process(Order order) {
if (order.getItems().isEmpty()) {
// 空订单没有意义,拒绝处理
throw new EmptyOrderException(order.getId());
}
// 价格验证:确保订单项的价格与当前价格一致
// 如果不一致,抛出 PriceChangedException 让调用方决定
validatePrices(order);
// 折扣计算在 Order.calculateTotal() 中处理
// 包括会员折扣、优惠券、满减等
order.calculateTotal();
orderRepository.save(order);
eventPublisher.publish(new OrderProcessed(order.getId()));
}四、本章小结
注释目的:描述代码不能表达的信息。
接口注释:做什么、参数约束、返回值、异常。
实现注释:为什么这样做、不明显的约束、警告。
不需要注释:代码已经清楚表达的信息。
五、思考与练习
审查你代码中的注释,哪些是好的,哪些是坏的?
如何判断一个注释是否有价值?
"代码即文档"的观点有什么局限?