重构是在不改变代码外部行为的前提下,改善其内部结构,使代码更清晰、更易维护、更易扩展。重构不是重写,也不是修bug,而是持续优化设计的过程。
一、什么时候该重构
- 重复代码:同一段逻辑出现在两处以上。
- 过长函数:一个函数做了太多事,难以理解。
- 过大类:类承担了过多职责,违反单一职责原则。
- 参数列表过长:函数参数超过3~4个,难以使用。
- 条件表达式过于复杂:if-else 嵌套深,逻辑不直观。
- 代码坏味道:如数据泥团、霰弹式修改、依恋情结等。
二、常用重构手法(实用举例)
- 提炼函数
将复杂的代码块提取为独立函数,用函数名解释意图。
// 重构前
function printOwing(invoice) {
console.log("---");
console.log("Customer: " + invoice.customer);
let outstanding = 0;
for (let o of invoice.orders) {
outstanding += o.amount;
}
console.log("Total: " + outstanding);
}
// 重构后
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding(invoice);
printDetails(invoice.customer, outstanding);
}
function printBanner() { console.log("---"); }
function calculateOutstanding(invoice) {
return invoice.orders.reduce((sum, o) => sum + o.amount, 0);
}
function printDetails(customer, total) {
console.log("Customer: " + customer);
console.log("Total: " + total);
}
- 引入参数对象
当一组参数总是一起传递时,用对象替代。
# 重构前
def create_appointment(start_date, start_time, end_date, end_time):
...
# 重构后
class TimeRange:
def __init__(self, start, end):
self.start = start
self.end = end
def create_appointment(time_range):
...
- 以多态取代条件表达式
用类和多态替代复杂的switch或if-else。
// 重构前
double getSpeed(Animal a) {
if (a.type == "bird") return a.baseSpeed - 5;
else if (a.type == "fish") return a.baseSpeed + 2;
else return a.baseSpeed;
}
// 重构后
abstract class Animal {
abstract double getSpeed();
}
class Bird extends Animal {
double getSpeed() { return baseSpeed - 5; }
}
class Fish extends Animal {
double getSpeed() { return baseSpeed + 2; }
}
- 简化条件表达式
- 合并条件:同一行为的不同条件合并为一个条件表达式。
- 分解条件:将复杂的条件提取为独立函数,用名称解释含义。
- 卫语句:处理特殊/异常情况后快速返回,减少嵌套。
// 卫语句示例
function getPayAmount() {
if (isDead) return deadAmount();
if (isSeparated) return separatedAmount();
if (isRetired) return retiredAmount();
return normalAmount();
}
- 搬移特性
将函数或字段移动到更合适的类或模块中,使职责内聚。例如,一个类频繁访问另一个类的数据,可将方法移到那个数据所在的类。
三、重构的安全步骤
- 确保有测试:重构前编写/运行单元测试,确保行为不变。
- 小步前进:每次只做一种重构,修改后立刻运行测试。
- 使用自动化工具:IDE重构功能(如重命名、提取函数、移动成员)可减少失误。
- 代码审查:提交前让同事审查,发现潜在问题。
四、真实场景建议
- 不要为了重构而重构:总与添加功能或修复bug结合进行(“营地法则”)。
- 优先重构高频修改的代码:修改越频繁,重构收益越大。
- 保持提交原子化:一次重构一次提交,附带清晰说明,便于回溯。
- 遗留代码先加测试再重构:没测试的遗留代码,先写特征测试固定住现有行为,再动手。
小结:重构是开发者每天都要做的事情。养成随时重构的习惯,用小的、安全的步骤持续优化代码,就能避免陷入“大重构”和“技术债务”的困境。