重构Skill refactoring

重构是软件开发中的核心技能,用于在不改变外部行为的前提下,通过系统化技术如提取函数、移除重复、简化条件逻辑和应用设计模式,改进代码结构、可读性和可维护性。它能有效减少技术债务、提高代码质量、增强测试覆盖,并促进敏捷开发。关键词:代码重构、软件工程、代码优化、设计模式、技术债务、可维护性、测试驱动开发、代码审查。

架构设计 0 次安装 0 次浏览 更新于 3/18/2026

名称: 重构 描述: 在不改变外部行为的前提下,通过系统化重构技术如提取函数、移除重复、简化条件逻辑和应用设计模式,改进代码结构、可读性和可维护性。用于减少技术债务、提取函数或类、移除代码重复、简化复杂条件逻辑、重命名以增强清晰度、应用设计模式、改进代码组织、降低耦合、提高内聚,或在结构改进期间保持测试覆盖率。

重构 - 在不改变行为的情况下改进代码结构

何时使用此技能

  • 在现有代码库中减少技术债务
  • 从长方法中提取函数
  • 移除代码重复(DRY原则)
  • 简化复杂的条件逻辑
  • 重命名变量、函数以增强清晰度
  • 应用设计模式以改进结构
  • 改进代码组织和文件结构
  • 降低模块间的耦合
  • 提高类/模块内的内聚
  • 在重构期间保持测试覆盖率
  • 拆分大文件或函数
  • 逐步现代化遗留代码

何时使用此技能

  • 代码工作正常但难以理解、修改或测试。在保持功能的同时改进结构。
  • 当处理相关任务或功能时
  • 在需要此专业知识的开发过程中

使用时机: 代码工作正常但难以理解、修改或测试。在保持功能的同时改进结构。

核心原则

  1. 行为保持 - 重构绝不改变代码的功能,只改变实现方式
  2. 小步骤 - 每次进行微小更改,并在每个步骤后测试
  3. 测试优先 - 在重构前确保有全面的测试
  4. 一次只做一件事 - 不要将重构与功能添加混在一起
  5. 在添加功能时重构 - 让代码比你找到时更好

何时重构

触发器

  • 在添加功能前 - 为新功能腾出空间
  • 在代码审查期间 - 发现代码异味或混乱部分
  • 在修复漏洞时 - 通过更好结构使漏洞不可能发生
  • 童子军规则 - 总是让代码比你找到时更整洁

红色标志(代码异味)

✗ 函数长度超过约50行
✗ 深层嵌套条件(>3层)
✗ 重复代码(复制粘贴编程)
✗ 不清晰的变量名(x, temp, data)
✗ 函数参数超过3个
✗ 上帝类(类做太多事)
✗ 长参数列表
✗ 注释解释代码功能(代码应自文档化)

重构目录

1. 提取方法/函数

时机: 函数做太多事或有复杂逻辑

// 之前 - 难以理解
function processOrder(order) {
  // 计算总额
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
    if (item.discount) {
      total -= item.price * item.quantity * item.discount;
    }
  }
  
  // 应用税费
  const tax = total * 0.08;
  total += tax;
  
  // 检查库存
  for (const item of order.items) {
    const stock = inventory.get(item.id);
    if (stock < item.quantity) {
      throw new Error('库存不足');
    }
  }
  
  return { total, tax };
}

// 之后 - 职责清晰
function processOrder(order) {
  validateInventory(order.items);
  const subtotal = calculateSubtotal(order.items);
  const tax = calculateTax(subtotal);
  const total = subtotal + tax;
  return { total, tax };
}

function calculateSubtotal(items) {
  return items.reduce((sum, item) => {
    const itemTotal = item.price * item.quantity;
    const discount = item.discount ? itemTotal * item.discount : 0;
    return sum + itemTotal - discount;
  }, 0);
}

function calculateTax(amount) {
  const TAX_RATE = 0.08;
  return amount * TAX_RATE;
}

function validateInventory(items) {
  for (const item of items) {
    const stock = inventory.get(item.id);
    if (stock < item.quantity) {
      throw new InsufficientStockError(item.id, stock, item.quantity);
    }
  }
}

2. 用命名常量替换魔术数字

// 之前 - 这些数字是什么意思?
if (user.age >= 18 && user.age <= 65) {
  premium = basePrice * 1.0;
} else {
  premium = basePrice * 1.5;
}

setTimeout(checkStatus, 60000);

// 之后 - 自文档化
const MIN_STANDARD_AGE = 18;
const MAX_STANDARD_AGE = 65;
const STANDARD_RATE_MULTIPLIER = 1.0;
const HIGH_RISK_RATE_MULTIPLIER = 1.5;
const STATUS_CHECK_INTERVAL_MS = 60 * 1000; // 1分钟

if (user.age >= MIN_STANDARD_AGE && user.age <= MAX_STANDARD_AGE) {
  premium = basePrice * STANDARD_RATE_MULTIPLIER;
} else {
  premium = basePrice * HIGH_RISK_RATE_MULTIPLIER;
}

setTimeout(checkStatus, STATUS_CHECK_INTERVAL_MS);

3. 简化条件表达式

// 之前 - 复杂嵌套条件
function getShippingCost(order) {
  if (order.items.length > 0) {
    if (order.total > 50) {
      if (order.isPremium) {
        return 0;
      } else {
        return 5;
      }
    } else {
      if (order.isPremium) {
        return 5;
      } else {
        return 10;
      }
    }
  } else {
    return 0;
  }
}

// 之后 - 守卫子句和提前返回
function getShippingCost(order) {
  if (order.items.length === 0) return 0;
  if (order.isPremium && order.total > 50) return 0;
  if (order.isPremium) return 5;
  if (order.total > 50) return 5;
  return 10;
}

// 更好 - 策略模式
const SHIPPING_RATES = {
  premiumOverFifty: { cost: 0, applies: (o) => o.isPremium && o.total > 50 },
  premium: { cost: 5, applies: (o) => o.isPremium },
  standardOverFifty: { cost: 5, applies: (o) => o.total > 50 },
  standard: { cost: 10, applies: () => true }
};

function getShippingCost(order) {
  if (order.items.length === 0) return 0;
  
  for (const rate of Object.values(SHIPPING_RATES)) {
    if (rate.applies(order)) return rate.cost;
  }
}

4. 提取变量以增强清晰度

// 之前 - 难以理解
if (
  (platform === 'ios' && version >= 13) ||
  (platform === 'android' && version >= 10) ||
  (platform === 'web' && browserVersion >= 90)
) {
  enableNewUI();
}

// 之后 - 意图揭示命名
const isIosSupportedVersion = platform === 'ios' && version >= 13;
const isAndroidSupportedVersion = platform === 'android' && version >= 10;
const isWebSupportedVersion = platform === 'web' && browserVersion >= 90;
const supportsNewUI = 
  isIosSupportedVersion || 
  isAndroidSupportedVersion || 
  isWebSupportedVersion;

if (supportsNewUI) {
  enableNewUI();
}

5. 移除死代码

// 之前 - 杂有无用代码
function calculatePrice(item) {
  let price = item.basePrice;
  
  // 遗留折扣系统(不再使用)
  // if (item.category === 'electronics') {
  //   price *= 0.9;
  // }
  
  // 应用当前折扣
  if (item.discount) {
    price *= (1 - item.discount);
  }
  
  // 旧税费计算
  // const oldTax = price * 0.05;
  
  // 新税费计算
  const tax = price * 0.08;
  
  return price + tax;
}

// 之后 - 整洁聚焦
function calculatePrice(item) {
  let price = item.basePrice;
  
  if (item.discount) {
    price *= (1 - item.discount);
  }
  
  const TAX_RATE = 0.08;
  const tax = price * TAX_RATE;
  
  return price + tax;
}

6. 用守卫子句替换嵌套条件

// 之前 - 深层嵌套
function withdraw(account, amount) {
  if (account.isActive) {
    if (account.balance >= amount) {
      if (amount > 0) {
        if (!account.isFrozen) {
          account.balance -= amount;
          return { success: true, newBalance: account.balance };
        } else {
          return { success: false, error: '账户冻结' };
        }
      } else {
        return { success: false, error: '无效金额' };
      }
    } else {
      return { success: false, error: '资金不足' };
    }
  } else {
    return { success: false, error: '账户未激活' };
  }
}

// 之后 - 守卫子句
function withdraw(account, amount) {
  if (!account.isActive) {
    return { success: false, error: '账户未激活' };
  }
  
  if (account.isFrozen) {
    return { success: false, error: '账户冻结' };
  }
  
  if (amount <= 0) {
    return { success: false, error: '无效金额' };
  }
  
  if (account.balance < amount) {
    return { success: false, error: '资金不足' };
  }
  
  account.balance -= amount;
  return { success: true, newBalance: account.balance };
}

7. 用多态替换类型代码

// 之前 - 到处都是类型检查
class Employee {
  constructor(type, salary) {
    this.type = type; // 'engineer', 'manager', 'salesperson'
    this.salary = salary;
  }
  
  calculateBonus() {
    if (this.type === 'engineer') {
      return this.salary * 0.1;
    } else if (this.type === 'manager') {
      return this.salary * 0.2;
    } else if (this.type === 'salesperson') {
      return this.salary * 0.15;
    }
  }
  
  getTitle() {
    if (this.type === 'engineer') {
      return '软件工程师';
    } else if (this.type === 'manager') {
      return '工程经理';
    } else if (this.type === 'salesperson') {
      return '销售代表';
    }
  }
}

// 之后 - 多态
class Employee {
  constructor(salary) {
    this.salary = salary;
  }
  
  calculateBonus() {
    throw new Error('必须由子类实现');
  }
  
  getTitle() {
    throw new Error('必须由子类实现');
  }
}

class Engineer extends Employee {
  calculateBonus() {
    return this.salary * 0.1;
  }
  
  getTitle() {
    return '软件工程师';
  }
}

class Manager extends Employee {
  calculateBonus() {
    return this.salary * 0.2;
  }
  
  getTitle() {
    return '工程经理';
  }
}

class Salesperson extends Employee {
  calculateBonus() {
    return this.salary * 0.15;
  }
  
  getTitle() {
    return '销售代表';
  }
}

8. 合并重复代码

// 之前 - 重复逻辑
function calculateEmployeeBonus(employee) {
  let bonus = employee.salary * 0.1;
  if (employee.yearsOfService > 5) {
    bonus += 1000;
  }
  if (employee.hasTopPerformance) {
    bonus *= 1.5;
  }
  return bonus;
}

function calculateContractorBonus(contractor) {
  let bonus = contractor.salary * 0.1;
  if (contractor.yearsOfService > 5) {
    bonus += 1000;
  }
  if (contractor.hasTopPerformance) {
    bonus *= 1.5;
  }
  return bonus;
}

// 之后 - 共享逻辑
function calculateBonus(person) {
  let bonus = person.salary * 0.1;
  
  if (person.yearsOfService > 5) {
    bonus += 1000;
  }
  
  if (person.hasTopPerformance) {
    bonus *= 1.5;
  }
  
  return bonus;
}

// 用于两者
const employeeBonus = calculateBonus(employee);
const contractorBonus = calculateBonus(contractor);

重构过程

逐步方法

1. 识别异味或改进机会
2. 确保测试存在并通过
3. 进行一次小重构更改
4. 运行测试 - 必须仍通过
5. 提交(可选但推荐)
6. 重复步骤3-5直到满意
7. 最终测试运行
8. 代码审查

示例过程

// 原始代码
function process(data) {
  let result = [];
  for (let i = 0; i < data.length; i++) {
    if (data[i].status === 'active' && data[i].age >= 18) {
      result.push({
        id: data[i].id,
        name: data[i].name,
        email: data[i].email
      });
    }
  }
  return result;
}

// 步骤1: 提取条件
function isEligible(item) {
  return item.status === 'active' && item.age >= 18;
}

function process(data) {
  let result = [];
  for (let i = 0; i < data.length; i++) {
    if (isEligible(data[i])) {
      result.push({
        id: data[i].id,
        name: data[i].name,
        email: data[i].email
      });
    }
  }
  return result;
}
// 测试 ✓

// 步骤2: 用数组方法替换循环
function process(data) {
  let result = [];
  data.forEach(item => {
    if (isEligible(item)) {
      result.push({
        id: item.id,
        name: item.name,
        email: item.email
      });
    }
  });
  return result;
}
// 测试 ✓

// 步骤3: 提取转换
function transformToUser(item) {
  return {
    id: item.id,
    name: item.name,
    email: item.email
  };
}

function process(data) {
  let result = [];
  data.forEach(item => {
    if (isEligible(item)) {
      result.push(transformToUser(item));
    }
  });
  return result;
}
// 测试 ✓

// 步骤4: 使用filter和map
function process(data) {
  return data
    .filter(isEligible)
    .map(transformToUser);
}
// 测试 ✓

// 最终结果 - 清晰、函数式、可测试

常见陷阱

❌ 无测试重构

如果测试不存在,先写测试!
否则,无法验证行为保持。

❌ 大爆炸重构

不要一次重写所有内容。
小的、增量的更改更安全且易于审查。

❌ 混用重构与功能添加

分开提交:
- 提交1: 重构(无行为改变)
- 提交2: 添加功能(使用重构后代码)

❌ 过度工程

不要为“未来需求”添加复杂性
在需要时重构,而不是推测性重构。
YAGNI: 你不会需要它

工具

IDE重构工具

✓ 重命名(跨所有文件安全重命名)
✓ 提取方法/函数
✓ 提取变量
✓ 内联变量/函数
✓ 更改签名
✓ 移动到文件/模块

静态分析

# JavaScript/TypeScript
eslint --fix
prettier --write

# Python
pylint
black

# 查找代码异味
# PMD, SonarQube, CodeClimate

资源


记住: 重构是持续改进。每次接触代码时,让它变得更整洁一点。小且频繁的重构比偶尔的大规模重写更好。