Java遗留代码重构:逐步指南
处理Java遗留代码是开发人员面临的最常见且复杂的任务之一。这类代码通常缺乏测试,采用单体风格编写,并包含许多反模式。然而,完全重写系统成本高昂且风险巨大。更具效率的方法是进行系统性的重构。在本文中,我们将探讨重构Java遗留代码的实用方法,使代码更清晰、更安全且更易于维护。
1. 准备工作:分析与构建“安全网”
在修改代码之前,必须理解其功能以及如何不破坏现有逻辑。重构遗留代码的主要原则是不改变行为。为此,需要创建一个“安全网”——一套测试用例。
从研究代码开始。找到入口点(从外部调用的方法),并尝试确定预期的结果。即使代码编写得很糟糕,它也能正确完成某些功能。
分析之后,编写特征测试。这些测试用于记录代码的当前行为。它们不验证逻辑是否正确,而只是记录当前的情况。
示例:我们有一个处理订单的“怪物”类。
public class OrderProcessor { public void process(Order order) { // 200行意大利面条式代码 if (order.getTotal() > 100) { order.setStatus("VIP"); } else { order.setStatus("STANDARD"); } // ... 许多其他逻辑 }}我们不知道内部细节,但可以编写一个测试:
@Testpublic void testProcessOrderWithHighTotal() { // 给定 Order order = new Order(); order.setTotal(150); OrderProcessor processor = new OrderProcessor();
// 执行 processor.process(order);
// 然后(记录当前行为) assertEquals("VIP", order.getStatus());}运行测试。如果通过,说明你已经记录了当前行为。现在你有了防止回归的保护。
2. 提取方法
对于大方法来说,这是最安全且最有效的重构。思路很简单:取出一段完成单一功能的代码,将其提取到一个名称明确的方法中。
考虑一个糟糕代码的示例:
public double calculateEmployeePay(Employee emp) { double basePay = emp.getBasePay(); double overtimePay = 0; if (emp.getHoursWorked() > 40) { overtimePay = (emp.getHoursWorked() - 40) * basePay * 1.5; } double tax = basePay * 0.13; double insurance = 5000; return basePay + overtimePay - tax - insurance;}这里,加班费、税款和保险的计算混合在一起。让我们将它们分离:
public double calculateEmployeePay(Employee emp) { double basePay = emp.getBasePay(); double overtimePay = calculateOvertimePay(emp, basePay); double tax = calculateTax(basePay); double insurance = getInsuranceCost(); return basePay + overtimePay - tax - insurance;}
private double calculateOvertimePay(Employee emp, double basePay) { if (emp.getHoursWorked() > 40) { return (emp.getHoursWorked() - 40) * basePay * 1.5; } return 0;}
private double calculateTax(double basePay) { return basePay * 0.13;}
private double getInsuranceCost() { return 5000;}现在每个方法只负责一个任务。代码变得更易读,且便于单独测试。
3. 消除重复(DRY原则)
遗留代码经常因复制粘贴而出现问题。同一段逻辑可能出现在不同的类中。这是危险的:当需求发生变化时,你可能会忘记修改其中一个副本。
假设两个不同的服务