refactor-legacy-code refactor-legacy-code

系统地重构遗留代码以提高代码的可维护性、可读性和性能,同时保持现有功能。

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

重构遗留代码

概览

这项技能帮助您系统地重构遗留代码,以提高可维护性、可读性和性能,同时保持现有功能。它遵循安全重构的行业最佳实践,并进行全面测试。

何时使用

  • 现代化过时的代码模式或弃用的API
  • 减少现有代码库的技术债务
  • 提高代码的可读性和可维护性
  • 从单体代码中提取可重用组件
  • 升级到更新的语言特性或框架
  • 为新功能开发做准备

指令

1. 代码评估

首先,分析遗留代码以理解:

# 审查代码库结构
tree -L 3 -I 'node_modules|dist|build'

# 检查过时的依赖
npm outdated  # 或 pip list --outdated, composer outdated, 等。

# 识别代码复杂性热点
# 使用工具:
# - SonarQube 用于代码异味
# - eslint 用于 JavaScript
# - pylint 用于 Python
# - RuboCop 用于 Ruby

评估清单:

  • [ ] 识别过时的模式和API
  • [ ] 定位紧密耦合的组件
  • [ ] 查找重复的代码块
  • [ ] 查看测试覆盖率差距
  • [ ] 文档化当前行为和边缘情况
  • [ ] 识别性能瓶颈

2. 建立安全网

重构前,确保您有全面的测试:

// 添加特性化测试以锁定当前行为
describe('LegacyFeature', () => {
  it('should preserve existing behavior during refactoring', () => {
    // 测试当前实现行为
    const input = { /* 实际测试数据 */ };
    const result = legacyFunction(input);

    // 文档化预期输出
    expect(result).toEqual({ /* 当前实际输出 */ });
  });
});

测试策略:

  • 添加关键路径的单元测试
  • 创建组件交互的集成测试
  • 文档化边缘情况和错误场景
  • 设置测试覆盖率监控
  • 每次重构步骤前运行测试

3. 增量重构

系统地应用重构模式:

提取函数/方法

// 之前:长且复杂的函数
function processUserData(user) {
  // 50行混合验证、转换和业务逻辑
  if (!user.email || !user.email.includes('@')) return null;
  const normalized = user.email.toLowerCase().trim();
  // ... 更复杂的逻辑
}

// 之后:提取的、专注的函数
function validateEmail(email) {
  return email && email.includes('@');
}

function normalizeEmail(email) {
  return email.toLowerCase().trim();
}

function processUserData(user) {
  if (!validateEmail(user.email)) return null;
  const email = normalizeEmail(user.email);
  // 清晰、可读的流程
}

用多态替换条件语句

# 之前:复杂的条件逻辑
def calculate_price(customer_type, base_price):
    if customer_type == 'regular':
        return base_price
    elif customer_type == 'premium':
        return base_price * 0.9
    elif customer_type == 'vip':
        return base_price * 0.8
    else:
        return base_price

# 之后:多态方法
class PricingStrategy:
    def calculate(self, base_price):
        return base_price

class RegularPricing(PricingStrategy):
    pass

class PremiumPricing(PricingStrategy):
    def calculate(self, base_price):
        return base_price * 0.9

class VIPPricing(PricingStrategy):
    def calculate(self, base_price):
        return base_price * 0.8

# 使用
pricing = pricing_strategies[customer_type]
price = pricing.calculate(base_price)

引入参数对象

// 之前:长参数列表
function createUser(
  firstName: string,
  lastName: string,
  email: string,
  phone: string,
  address: string,
  city: string,
  state: string,
  zip: string
) {
  // ...
}

// 之后:参数对象
interface UserData {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  address: Address;
}

interface Address {
  street: string;
  city: string;
  state: string;
  zip: string;
}

function createUser(userData: UserData) {
  // ...
}

4. 现代化模式

用现代等价物替换过时的模式:

用Promises代替Callbacks

// 之前:回调地狱
function fetchUserData(userId, callback) {
  db.query('SELECT * FROM users WHERE id = ?', [userId], (err, user) => {
    if (err) return callback(err);
    db.query('SELECT * FROM orders WHERE user_id = ?', [userId], (err, orders) => {
      if (err) return callback(err);
      callback(null, { user, orders });
    });
  });
}

// 之后:Async/await
async function fetchUserData(userId) {
  const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
  const orders = await db.query('SELECT * FROM orders WHERE user_id = ?', [userId]);
  return { user, orders };
}

现代语言特性

// 之前:var 和字符串连接
var userName = user.firstName + ' ' + user.lastName;
var isActive = user.status === 'active' ? true : false;

// 之后:const/let 和模板字面量
const userName = `${user.firstName} ${user.lastName}`;
const isActive = user.status === 'active';

5. 减少依赖

打破紧密耦合:

# 之前:紧密耦合到特定实现
class OrderProcessor:
    def __init__(self):
        self.db = MySQLDatabase()  # 紧密耦合
        self.email = SendGridEmail()  # 紧密耦合

    def process_order(self, order):
        self.db.save(order)
        self.email.send(order.customer_email, "Order confirmed")

# 之后:依赖注入
class OrderProcessor:
    def __init__(self, database, email_service):
        self.db = database  # 任何数据库实现
        self.email = email_service  # 任何电子邮件服务

    def process_order(self, order):
        self.db.save(order)
        self.email.send(order.customer_email, "Order confirmed")

# 易于测试,使用mocks
processor = OrderProcessor(MockDatabase(), MockEmailService())

6. 文档化

记录重构决策:

## 重构日志

### 2025-01-15: 提取支付处理
**理由**:支付逻辑嵌入在订单控制器中(500行)
**变更**:
- 提取了具有单一责任的PaymentService
- 引入了PaymentGateway接口以增加灵活性
- 添加了全面的单元测试(95%覆盖率)
**破坏性变更**:无(仅限内部重构)
**性能影响**:订单处理时间提高了15%

### 2025-01-16: 用Async/Await替换回调
**理由**:用户认证流程中的回调地狱
**变更**:
- 将所有认证方法转换为async/await
- 使用try/catch简化错误处理
- 提高可读性(从150行减少到80行)
**破坏性变更**:函数签名更改(需要在调用代码中更新)
**迁移**:更新了控制器中的所有12个调用点

最佳实践

✅ 要做

  • 增量重构:小的、可测试的变更
  • 频繁运行测试:每次重构步骤后
  • 经常提交:创建逻辑上的、原子的提交
  • 保持现有测试通过:不要破坏功能
  • 使用IDE重构工具:比手动编辑更安全
  • 审查代码覆盖率:确保测试覆盖重构后的代码
  • 记录决策:为什么,而不仅仅是什么
  • 寻求同行评审:新的眼光能发现问题

❌ 不要做

  • 将重构与新功能混合:分离关注点
  • 无测试重构:破坏性变更的食谱
  • 改变行为:重构应保持功能
  • 重构大块代码:增加风险和审查难度
  • 忽视代码异味:系统地解决它们
  • 跳过文档:未来的维护者需要上下文

常见陷阱

1. 过度工程化

// ❌ 对于简单情况太复杂
class UserNameFormatterFactory {
  createFormatter(type) {
    return new UserNameFormatter(new FormattingStrategy(type));
  }
}

// ✅ 适合简单情况
function formatUserName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

2. 过早优化

先关注可读性,然后根据分析优化瓶颈。

3. 破坏向后兼容性

在移除公共API之前使用弃用警告:

/** @deprecated Use createUser(userData) instead. Will be removed in v2.0 */
function createUserOld(firstName: string, lastName: string, email: string) {
  console.warn('createUserOld is deprecated. Use createUser(userData)');
  return createUser({ firstName, lastName, email });
}

测试策略

单元测试

describe('Refactored User Service', () => {
  describe('validateEmail', () => {
    it('should accept valid email formats', () => {
      expect(validateEmail('user@example.com')).toBe(true);
    });

    it('should reject invalid email formats', () => {
      expect(validateEmail('invalid')).toBe(false);
      expect(validateEmail('')).toBe(false);
      expect(validateEmail(null)).toBe(false);
    });
  });
});

集成测试

def test_refactored_order_processing():
    """确保重构后的代码保持端到端行为"""
    # 安排
    order = create_test_order()
    processor = OrderProcessor(test_database, test_email_service)

    # 行动
    result = processor.process_order(order)

    # 断言
    assert result.status == 'completed'
    assert test_database.orders.count() == 1
    assert test_email_service.sent_count == 1

回归测试

运行完整的测试套件以确保没有意外的副作用。

重构模式参考

常见模式

  1. 提取方法/函数:将长函数分解为更小的函数
  2. 提取类:将相关功能分组
  3. 内联方法:移除不必要的间接性
  4. 移动方法:将方法放在适当的类中
  5. 重命名:使用描述性名称
  6. 替换魔法数字:使用命名常量
  7. 用多态替换条件语句:使用继承
  8. 引入参数对象:将相关参数分组
  9. 移除重复:DRY原则
  10. 简化条件逻辑:降低复杂性

工具与资源

静态分析工具

  • JavaScript/TypeScript:ESLint, TSLint, SonarQube
  • Python:Pylint, Flake8, Bandit
  • Java:SonarQube, PMD, Checkstyle
  • Ruby:RuboCop, Reek
  • PHP:PHPStan, Psalm

IDE重构支持

  • VS Code:内置重构命令
  • JetBrains IDEs:全面的重构工具
  • Eclipse:自动化重构
  • Vim/Neovim:语言服务器重构操作

推荐阅读

  • 《重构》 马丁·福勒
  • 《与遗留代码共存》 迈克尔·费瑟斯
  • 《代码整洁之道》 罗伯特·C·马丁

示例

完整重构示例

之前

// legacy-user-service.js - 200行复杂、耦合的代码
var UserService = {
  createUser: function(fn, ln, em, ph, addr) {
    if (!em || em.indexOf('@') === -1) {
      return { error: 'Invalid email' };
    }
    var conn = mysql.createConnection(config);
    conn.connect();
    conn.query(
      'INSERT INTO users (first_name, last_name, email, phone, address) VALUES (?, ?, ?, ?, ?)',
      [fn, ln, em.toLowerCase(), ph, addr],
      function(err, result) {
        if (err) {
          console.log(err);
          return { error: 'Database error' };
        }
        // 发送欢迎电子邮件
        var nodemailer = require('nodemailer');
        var transporter = nodemailer.createTransport(emailConfig);
        transporter.sendMail({
          to: em,
          subject: 'Welcome!',
          html: '<h1>Welcome ' + fn + '!</h1>'
        }, function(err, info) {
          if (err) console.log(err);
        });
        conn.end();
        return { id: result.insertId };
      }
    );
  }
};

之后

// user-service.ts - 干净、可测试、可维护
interface UserData {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  address: string;
}

class UserService {
  constructor(
    private database: Database,
    private emailService: EmailService,
    private validator: Validator
  ) {}

  async createUser(userData: UserData): Promise<User> {
    this.validator.validateEmail(userData.email);

    const normalizedData = this.normalizeUserData(userData);
    const user = await this.database.users.create(normalizedData);

    await this.sendWelcomeEmail(user);

    return user;
  }

  private normalizeUserData(data: UserData): UserData {
    return {
      ...data,
      email: data.email.toLowerCase().trim()
    };
  }

  private async sendWelcomeEmail(user: User): Promise<void> {
    await this.emailService.send({
      to: user.email,
      subject: 'Welcome!',
      template: 'welcome',
      data: { firstName: user.firstName }
    });
  }
}

// validator.ts
class Validator {
  validateEmail(email: string): void {
    if (!email || !email.includes('@')) {
      throw new ValidationError('Invalid email format');
    }
  }
}

// 易于测试
describe('UserService', () => {
  it('should create user with valid data', async () => {
    const mockDb = createMockDatabase();
    const mockEmail = createMockEmailService();
    const service = new UserService(mockDb, mockEmail, new Validator());

    const user = await service.createUser({
      firstName: 'John',
      lastName: 'Doe',
      email: 'john@example.com',
      phone: '555-0123',
      address: '123 Main St'
    });

    expect(user.id).toBeDefined();
    expect(mockDb.users.create).toHaveBeenCalled();
    expect(mockEmail.send).toHaveBeenCalledWith(
      expect.objectContaining({ to: 'john@example.com' })
    );
  });
});

实现的好处

  • 可测试性:依赖注入,易于模拟
  • 可读性:清晰、专注的方法
  • 可维护性:单一责任原则
  • 类型安全:TypeScript接口防止错误
  • 可重用性:组件可以独立使用
  • 错误处理:适当的异常处理
  • 现代模式:Async/await, 依赖注入

清单

在考虑重构完成之前:

  • [ ] 所有现有测试通过
  • [ ] 为重构的代码添加了新测试
  • [ ] 代码覆盖率保持或提高
  • [ ] 公共API没有破坏性变更(或已适当记录)
  • [ ] 性能基准测试显示没有回归
  • [ ] 代码审查完成
  • [ ] 文档更新
  • [ ] 重构决策记录
  • [ ] CI/CD流水线通过
  • [ ] 在生产环境类似的环境进行阶段性部署以验证

支持

对于复杂的重构场景,考虑:

  • 尽早并经常寻求同行评审
  • 使用功能标志进行逐步推出
  • 创建重构计划文档
  • 安排专门的重构时间
  • 部署后监控生产指标