重构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

资源


记住: 重构是持续改进。每次接触代码时,让它变得更好一点。频繁的小重构比罕见的大重写更好。