重构遗留Java代码:从A到Z
处理遗留Java代码是每位开发者都会面临的挑战。旧代码往往缺乏测试、包含过时的模式且难以修改。然而,完全重写项目并不总是最佳解决方案。在本文中,我们将解析一种经过验证的遗留代码重构方法:从分析到实施变更,并附带Java具体示例。
1. 诊断与准备:在接触代码之前
第一步也是最重要的一步是了解你正在处理的内容。不要通过杂乱无章地编辑代码行来开始重构。而是:
- 依赖关系图:可视化哪些类依赖于哪些类。使用IntelliJ IDEA依赖查看器或JDepend等工具。
- 特征测试:编写能够捕获代码当前行为的测试。这是你的安全网。
- 识别入口点:确定哪些方法被最频繁调用,以及主要变更发生在何处。
针对需要重构的方法的一个简单特征测试示例:
import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.assertEquals;
public class LegacyCalculatorTest {
@Test public void testLegacyCalculate() { LegacyCalculator calc = new LegacyCalculator(); // Capture current behavior int result = calc.calculate(10, 5, "add"); assertEquals(15, result, "Current behavior: addition"); result = calc.calculate(10, 5, "sub"); assertEquals(5, result, "Current behavior: subtraction"); }}这样的测试并不从绝对意义上验证逻辑的正确性——它只是记录代码当前的功能。重构后,测试必须通过,否则你就改变了行为。
2. "外科手术式"重构策略:逐个方法进行
试图一次性重构整个类,是引入缺陷的必然途径。采用"战斗考古学"方法:
- 选择一个方法或一组相关的小方法。
- 为它们编写特征测试(如果尚不存在)。
- 应用一种重构技术(例如提取方法或重命名变量)。
- 运行测试。它们应该通过。
- 使用清晰的描述提交变更。
示例:重构一个包含"魔法数字"和长参数列表的方法。
原始代码(不良):
public class OrderProcessor { public double process(double amount, int type, double discount) { double result; if (type == 1) { result = amount * 1.2; // 20% markup if (discount > 0.1) { result = result - result * 0.05; // 5% discount } } else if (type == 2) { result = amount * 1.5; // 50% markup } else { result = amount; } return result; }}重构后(改进版):
public class OrderProcessor { private static final double PREMIUM_MARKUP = 1.2; private static final double STANDARD_MARKUP = 1.5; private static final double LOYALTY_DISCOUNT = 0.05; private static final double DISCOUNT_THRESHOLD = 0.1;
public double process(OrderType type, double amount, double discount) { double basePrice = applyMarkup(amount, type); return applyLoyaltyDiscount(basePrice, discount); }
private double applyMarkup(double amount, OrderType type) { return switch (type) { case PREMIUM -> amount * PREMIUM_MARKUP; case STANDARD -> amount * STANDARD_MARKUP; case BASIC