13.3 代码重构
1. 代码重构概述
代码重构是指在不改变软件外部行为的前提下,对代码内部结构进行调整和优化的过程。重构的目的是提高代码的可读性、可维护性和可扩展性,同时降低技术债务。
重构的核心原则:
- 不改变功能:重构前后代码的外部行为必须保持一致
- 小步前进:每次只进行小的、可控的修改
- 频繁测试:确保每次重构后功能仍然正常
- 持续进行:重构应该成为开发流程的常规部分
2. 常见重构场景与技术
2.1 命名重构
// 重构前
public int Calc(int a, int b) { ... }
// 重构后
public int CalculateSum(int firstNumber, int secondNumber) { ... }
2.2 方法重构
- 提取方法:将长方法分解为多个小方法
- 内联方法:将过于简单的方法调用替换为方法体
- 参数重构:添加/移除参数,参数对象化
// 提取方法示例
public void ProcessOrder(Order order) {
// 重构前:所有逻辑在一个方法中
if (order.IsValid) {
// 验证逻辑...
// 计算逻辑...
// 保存逻辑...
}
// 重构后
if (order.IsValid) {
ValidateOrder(order);
CalculateOrderTotal(order);
SaveOrder(order);
}
}
2.3 类重构
- 提取类:将大类的职责分解到多个小类
- 内联类:将过于简单的类合并到使用它的类中
- 继承体系重构:上拉/下推字段/方法
// 提取类示例
// 重构前
public class OrderProcessor {
public void Process(Order order) { ... }
public void PrintReceipt(Order order) { ... } // 打印职责不属于此类
}
// 重构后
public class OrderProcessor {
public void Process(Order order) { ... }
}
public class ReceiptPrinter {
public void Print(Order order) { ... }
}
3. C#特有的重构技术
3.1 使用现代C#特性重构旧代码
// 重构前:空值检查
if (customer != null && customer.Address != null) {
var city = customer.Address.City;
// ...
}
// 重构后:使用空条件运算符
var city = customer?.Address?.City;
if (city != null) {
// ...
}
3.2 LINQ重构
// 重构前:传统循环
List<string> names = new List<string>();
foreach (var person in people) {
if (person.Age > 18) {
names.Add(person.Name);
}
}
// 重构后:使用LINQ
var names = people.Where(p => p.Age > 18)
.Select(p => p.Name)
.ToList();
3.3 模式匹配重构
// 重构前:类型检查和转换
if (shape is Circle) {
var circle = (Circle)shape;
return circle.Radius * circle.Radius * Math.PI;
}
else if (shape is Rectangle) {
var rect = (Rectangle)shape;
return rect.Width * rect.Height;
}
// 重构后:使用模式匹配
return shape switch {
Circle c => c.Radius * c.Radius * Math.PI,
Rectangle r => r.Width * r.Height,
_ => throw new UnknownShapeException()
};
4. 重构工具支持
4.1 Visual Studio重构功能
- 快速操作:Ctrl+. 触发重构建议
- 重命名:F2 或 Ctrl+R,Ctrl+R
- 提取方法:Ctrl+R,Ctrl+M
- 提取接口:从类定义中提取接口
4.2 ReSharper和Roslyn分析器
- 提供更高级的重构建议
- 检测代码异味并提出解决方案
- 自动化重构操作
5. 重构最佳实践
- 测试先行:确保有良好的测试覆盖率后再重构
- 版本控制:频繁提交小步骤的重构
- 代码审查:重构后的代码应该经过同行评审
- 性能考量:某些重构可能影响性能,需要权衡
- 文档更新:重构后更新相关文档和注释
6. 重构案例研究
案例:电商订单处理系统重构
// 重构前
public class OrderService {
public void ProcessOrder(Order order) {
// 200行代码,包含:
// - 验证逻辑
// - 价格计算
// - 库存检查
// - 支付处理
// - 物流安排
// - 通知发送
}
}
// 重构后
public class OrderService {
private readonly IValidator _validator;
private readonly IPricingCalculator _calculator;
private readonly IInventoryService _inventory;
private readonly IPaymentProcessor _payment;
private readonly IShippingService _shipping;
private readonly INotificationService _notification;
public void ProcessOrder(Order order) {
_validator.Validate(order);
_calculator.Calculate(order);
_inventory.Reserve(order);
_payment.Process(order);
_shipping.Arrange(order);
_notification.Send(order);
}
}
7. 重构与设计模式
重构常常会自然地引导出设计模式的应用:
- 大量条件语句 → 策略模式/状态模式
- 多重嵌套 → 装饰器模式/责任链模式
- 复杂对象创建 → 工厂模式/建造者模式
- 紧耦合 → 观察者模式/中介者模式
通过持续重构,代码会逐渐演化出更优雅的设计,而不是一开始就强制应用设计模式。
8. 重构的挑战与解决方案
| 挑战 | 解决方案 |
|---|---|
| 缺乏时间 | 将重构作为开发的一部分,而非额外工作 |
| 害怕破坏现有功能 | 建立完善的测试套件 |
| 遗留代码难以理解 | 先添加测试,再小步重构 |
| 团队抵制 | 展示重构带来的长期收益 |
记住:重构不是奢侈品,而是必需品。定期重构可以减少bug,提高开发速度,最终节省更多时间。
