代码重构的艺术:让代码更优雅
重构不是重写,而是在不改变代码外部行为的前提下,改善其内部结构。好的重构就像整理房间——东西还是那些东西,但找起来更快、用起来更顺手。本文将介绍实用的重构技巧和原则。
一、何时需要重构
识别代码坏味道是重构的第一步:
// 坏味道1:过长函数
function processOrder(order) {
// 200行代码,做了验证、计算、保存、发邮件、记日志...
}
// 重构:拆分为小函数
function processOrder(order) {
validateOrder(order);
var total = calculateTotal(order);
saveOrder(order, total);
sendConfirmation(order);
logOrder(order);
}
// 坏味道2:嵌套过深
if (user) {
if (user.isActive()) {
if (user.hasPermission("edit")) {
// 实际逻辑
}
}
}
// 重构:提前返回(卫语句)
if (!user) return;
if (!user.isActive()) return;
if (!user.hasPermission("edit")) return;
// 实际逻辑
二、重构手法
// 1. 提取方法
// Before
function printReport(data) {
console.log("=== 报告 ===");
console.log("日期: " + new Date());
data.forEach(function(row) {
console.log(row.name + ": " + row.value);
});
}
// After
function printReport(data) {
printHeader();
printData(data);
}
// 2. 以多态替代条件
// Before
function getArea(shape) {
switch (shape.type) {
case "circle": return Math.PI * shape.r * shape.r;
case "rectangle": return shape.w * shape.h;
case "triangle": return shape.b * shape.h / 2;
}
}
// After
class Circle {
getArea() { return Math.PI * this.r * this.r; }
}
class Rectangle {
getArea() { return this.w * this.h; }
}
// 3. 替换临时变量
// Before
var basePrice = order.getBasePrice();
if (basePrice > 1000) {
return basePrice * 0.95;
}
// After - 使用查询方法
if (order.getBasePrice() > 1000) {
return order.getBasePrice() * 0.95;
}
三、重构安全网
// 重构前确保有测试
function testCalculateTotal() {
var order = new Order([
new Item("Book", 29.9, 2),
new Item("Pen", 5.0, 3),
]);
assertEqual(74.8, order.calculateTotal());
}
// 小步重构流程:
// 1. 添加测试
// 2. 运行测试(全绿)
// 3. 做一个小改动
// 4. 运行测试(应该还是全绿)
// 5. 重复3-4
// 使用版本控制
git checkout -b refactor/order-calculation
// 每完成一步重构就提交
git commit -m "refactor: 提取价格计算方法"
四、重构原则
- 童子军原则:让代码比你发现时更好一点
- 小步前进:每次只做一个小的重构
- 频繁测试:每一步重构后都运行测试
- 不要混合重构和新功能:重构的提交不应改变行为
- 重构与性能优化分开:先让代码清晰,再考虑优化
重构是一种习惯,不是一次性的活动。每次修改代码时,花几分钟让代码变得更好一点,长期积累下来,代码库的质量会有质的飞跃。记住:代码是写给人看的,顺便让机器执行。