第5章:安全重构(Safe Refactoring)
一、导读
1.1 本章学习目标
第5章讲解如何在遗留代码中安全地重构。
本章的核心学习目标包括:
- 掌握安全重构的原则
- 理解重构的时机和策略
- 学会使用渐进式改进
- 理解代码嗅探在重构中的作用
1.2 为什么本章如此重要
重构遗留代码是危险的——没有测试的保护,任何修改都可能引入 bug。本章教你如何在风险可控的前提下逐步改善代码质量。
1.3 核心问题引导
什么情况下应该重构遗留代码?
如何在没有测试的情况下安全重构?
重构的优先级如何确定?
渐进式改进和大规模重写各有什么优缺点?
二、核心概念详解
2.1 重构原则
安全重构原则:
1. 先添加测试,再重构
2. 小步前进:每次只做一个小改动
3. 频繁运行测试:确认每步都安全
4. 保持功能不变:重构不是添加功能
5. 回退能力:如果出错能快速恢复2.2 重构时机
应该重构的信号:
- 需要添加新功能但代码难以修改
- 修复 bug 时发现代码结构混乱
- 代码审查中发现重复代码
- 性能问题需要优化
不应该重构的情况:
- 代码即将被废弃
- 没有测试且无法添加测试
- 时间紧迫且风险太高2.3 渐进式改进
java
// 策略1:童子军规则
// "让营地比你发现时更干净"
// 每次修改代码时,顺手改善一小部分
// 策略2:绞杀者模式
// 逐步用新代码替换旧代码
public class OrderService {
private LegacyOrderProcessor legacy;
private NewOrderProcessor modern;
public void process(Order order) {
if (canHandleModern(order)) {
modern.process(order); // 新路径
} else {
legacy.process(order); // 旧路径
}
}
}
// 策略3:分支抽象
// 将变化点抽象出来
public class PriceCalculator {
public double calculate(Order order) {
double base = calculateBase(order);
double discount = calculateDiscount(order); // 抽象点
return base - discount;
}
}2.4 代码嗅探
遗留代码中的危险信号:
1. 超长方法(>50行)
2. 超大类(>500行)
3. 深层嵌套(>3层 if/for)
4. 重复代码
5. 魔法数字
6. 过长的参数列表
7. 全局变量
8. 硬编码的依赖三、实战案例
3.1 案例一:渐进式重构超长方法
java
// 遗留代码:500 行的 processOrder 方法
public void processOrder(Order order) {
// 验证
// 计算价格
// 检查库存
// 创建支付
// 发送通知
// 更新统计
// ...
}
// 第一步:提取注释标记的段落
public void processOrder(Order order) {
// === 验证 ===
validateOrder(order);
// === 计算价格 ===
double price = calculatePrice(order);
// === 检查库存 ===
checkInventory(order);
// === 创建支付 ===
Payment payment = createPayment(order, price);
// === 发送通知 ===
sendNotification(order, payment);
// === 更新统计 ===
updateStatistics(order);
}
// 第二步:逐步提取为独立方法
// 每提取一个方法就运行测试四、本章小结
重构原则:先测试、小步前进、频繁验证。
重构时机:需要修改、修复 bug、代码审查。
渐进改进:童子军规则、绞杀者模式、分支抽象。
代码嗅探:识别需要重构的危险信号。
五、思考与练习
制定一个遗留系统的渐进式重构计划。
绞杀者模式在什么场景下最有效?
如何在团队中推广安全重构的实践?