重构技巧:让烂代码重新焕发生命
代码不是写完就完事的,需要不断迭代改善。
Martin Fowler 说:"任何一个傻瓜都能写出计算机可以理解的代码,但只有写出人类能理解的代码,才是真正的程序员。"
重构,就是在不改变代码行为的前提下,让代码更易读、更易维护。
重构原则
- 测试先行:重构前确保有测试覆盖
- 小步前进:每次只改一点点,立即测试
- 保持功能:重构后行为不能变
常见重构手法
提取方法:长方法拆分
这是最常见的重构,把一个臃肿的方法拆成多个小方法:
java
// ❌ 重构前:方法做了太多事
public void processOrder(Order order) {
// 校验订单
if (order == null) throw new IllegalArgumentException();
if (order.getItems().isEmpty()) throw new IllegalArgumentException("订单项不能为空");
// 计算价格
double total = 0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
if (total > 1000) total *= 0.9;
// 保存
order.setTotal(total);
orderRepository.save(order);
// 发通知
notificationService.sendOrderConfirmation(order);
}
// ✅ 重构后:每个方法只做一件事
public void processOrder(Order order) {
validateOrder(order);
double total = calculateTotal(order);
applyDiscount(total);
saveAndNotify(order);
}
private void validateOrder(Order order) {
if (order == null) throw new IllegalArgumentException();
if (order.getItems().isEmpty())
throw new IllegalArgumentException("订单项不能为空");
}
private double calculateTotal(Order order) {
return order.getItems().stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
private void applyDiscount(double total) {
if (total > 1000) total *= 0.9;
}
private void saveAndNotify(Order order) {
orderRepository.save(order);
notificationService.sendOrderConfirmation(order);
}提取变量:简化复杂表达式
java
// ❌ 复杂表达式难以理解
if (order.getStatus() == OrderStatus.PAID &&
order.getAmount() > 1000 &&
order.getUser().isVip()) {
// 高级会员大额订单处理
}
// ✅ 提取变量:语义清晰
boolean isPaid = order.getStatus() == OrderStatus.PAID;
boolean isHighValue = order.getAmount() > 1000;
boolean isVip = order.getUser().isVip();
if (isPaid && isHighValue && isVip) {
// 高级会员大额订单处理
}引入参数对象:减少参数数量
java
// ❌ 参数过多
public void createUser(String firstName, String lastName, String email,
String phone, String address, int age, boolean isActive) {
}
// ✅ 用 DTO 封装
public void createUser(UserCreateDTO dto) {
}方法改名:让意图更清晰
java
// ❌ 模糊的方法名
public void process() { } // process 什么?
// ✅ 清晰的动词 + 名词
public void processOrder() { }
public void validateInput() { }
public void sendNotification() { }重构清单
开始重构前,先问自己:
| 问题 | 检查项 |
|---|---|
| 有测试吗? | 如果没有,先补测试 |
| 要改多少? | 超过 5 处就分多次改 |
| 命名清晰吗? | 新名字能见名知意吗 |
| 职责单一吗? | 一个方法只做一件事 |
总结
- 测试先行:没有测试保护的重构是冒险
- 小步快跑:每次只改一处,立即验证
- 命名要清:好名字是好代码的标志
- 先拆后改:把大方法拆小了,再改细节
重构是代码的日常维护,和写代码一样重要。
