正在加载,请稍候…

重构遗留代码:安全策略与技术

安全地重构遗留代码库而不破坏功能。学习绞杀者模式、接缝提取和增量改进策略。

Refactoring Legacy Code: Safe Strategies and Techniques

重构遗留代码:安全策略与技术

遗留代码是没有测试的代码——无论其存在多久。

黄金法则:没有测试就不要重构

// 步骤1:编写表征测试(捕获当前行为)
test('UserProcessor.process - 现有行为', () => {
  const processor = new UserProcessor(legacyDb);
  const result = processor.process(testUser);
  // 记录它做了什么,而不是它应该做什么
  expect(result.status).toBe('processed');
  expect(result.notificationCount).toBe(1);
});

// 步骤2:在测试的保护下进行重构
// 步骤3:验证测试仍然通过

Refactoring Legacy Code: Safe Strategies and Techniques illustration

绞杀者模式(Strangler Fig Pattern)

通过将新请求路由到新代码,逐步替换遗留系统。

// 遗留代码仍然存在
class LegacyPaymentProcessor {
  process(order: any) { /* 旧实现 */ }
}

// 新代码并行增长
class ModernPaymentService {
  async process(order: Order): Promise<PaymentResult> { /* 新实现 */ }
}

// 外观(Facade)在旧代码和新代码之间路由
class PaymentFacade {
  constructor(
    private legacy: LegacyPaymentProcessor,
    private modern: ModernPaymentService,
    private featureFlags: FeatureFlags
  ) {}

  async process(order: Order) {
    if (this.featureFlags.isEnabled('modern-payments')) {
      return await this.modern.process(order);
    }
    return this.legacy.process(order);
  }
}

Refactoring Legacy Code: Safe Strategies and Techniques illustration

提取接缝(Extract Seams)

找到可以在不修改代码的情况下改变行为的“接缝”。

// 难以测试:构造函数创建依赖
class ReportGenerator {
  private emailer = new SmtpEmailer(); // 接缝被阻塞

  generate(data: ReportData) {
    const report = this.buildReport(data);
    this.emailer.send(report);
  }
}

// 通过构造函数注入暴露接缝
class ReportGenerator {
  constructor(private emailer: Emailer) {} // 接缝暴露

  generate(data: ReportData) {
    const report = this.buildReport(data);
    this.emailer.send(report);
  }
}

// 现在可测试
const generator = new ReportGenerator(new MockEmailer());

Refactoring Legacy Code: Safe Strategies and Techniques illustration

增量改进清单

当处理遗留代码时:
1. 为即将修改的代码添加测试
2. 提取复杂块为方法
3. 重命名变量/方法使其更清晰
4. 删除死代码(先检查 git 历史)
5. 提取常量代替魔法数字
6. 简化嵌套条件

让营地比你发现时更干净。

打破依赖

// 依赖全局状态
class OrderService {
  save(order: Order) {
    Database.getInstance().save(order); // 全局单例
  }
}

// 打破依赖
class OrderService {
  constructor(private db: Database) {}

  save(order: Order) {
    this.db.save(order);
  }
}

// 生产代码中
const service = new OrderService(Database.getInstance());

// 测试中
const service = new OrderService(new InMemoryDatabase());

大类的分解

// 上帝类,做所有事情
class UserManager {
  register() { /* ... */ }
  login() { /* ... */ }
  sendEmail() { /* ... */ }
  generateReport() { /* ... */ }
  updateProfile() { /* ... */ }
  checkPermissions() { /* ... */ }
}

// 按职责分解
class AuthService { register() {} login() {} }
class UserNotificationService { sendEmail() {} }
class UserReportService { generateReport() {} }
class UserProfileService { updateProfile() {} }
class AuthorizationService { checkPermissions() {} }

重构不是一次性的重写——它是持续的、测试驱动的改进。