Refactoring von Legacy-Code in Java: Eine Schritt-für-Schritt-Anleitung
Die Arbeit mit Legacy-Code in Java ist eine der häufigsten und schwierigsten Aufgaben, mit denen Entwickler konfrontiert werden. Oft hat solcher Code keine Tests, ist monolithisch geschrieben und enthält viele Anti-Patterns. Das vollständige Neuschreiben des Systems ist jedoch teuer und riskant. Viel effektiver ist es, methodisches Refactoring anzuwenden. In diesem Artikel werden wir einen praktischen Ansatz zum Refactoring von Legacy-Java-Code besprechen, der ihn sauberer, sicherer und wartbarer macht.
1. Vorbereitung: Analyse und Erstellung eines "Sicherheitsnetzes"
Bevor Sie den Code ändern, müssen Sie verstehen, was er tut und wie Sie die bestehende Logik nicht beschädigen. Das Hauptprinzip des Refactorings von Legacy-Code ist, das Verhalten nicht zu ändern. Dafür müssen wir ein "Sicherheitsnetz" schaffen – eine Reihe von Tests.
Beginnen Sie mit dem Studium des Codes. Finden Sie die Einstiegspunkte (Methoden, die von außen aufgerufen werden) und versuchen Sie, die erwarteten Ergebnisse zu ermitteln. Auch wenn der Code schlecht geschrieben ist, macht er etwas richtig.
Schreiben Sie nach der Analyse charakterisierende Tests (Characterization Tests). Dies sind Tests, die das aktuelle Verhalten des Codes festhalten. Sie prüfen nicht, ob die Logik korrekt ist, sondern dokumentieren nur, was gerade passiert.
Beispiel: Wir haben eine Monster-Klasse, die Bestellungen verarbeitet.
public class OrderProcessor { public void process(Order order) { // 200 Zeilen Spaghetti-Code if (order.getTotal() > 100) { order.setStatus("VIP"); } else { order.setStatus("STANDARD"); } // ... viel andere Logik }}Wir wissen nicht, was drin ist, aber wir können einen Test schreiben:
@Testpublic void testProcessOrderWithHighTotal() { // Given Order order = new Order(); order.setTotal(150); OrderProcessor processor = new OrderProcessor();
// When processor.process(order);
// Then (aktuelles Verhalten festhalten) assertEquals("VIP", order.getStatus());}Führen Sie die Tests aus. Wenn sie bestanden werden, haben Sie das aktuelle Verhalten festgehalten. Jetzt haben Sie einen Schutz vor Regression.
2. Methode extrahieren (Extract Method)
Das sicherste und effektivste Refactoring für große Methoden. Die Idee ist einfach: Nehmen Sie ein Code-Fragment, das eine Sache tut, und extrahieren Sie es in eine separate Methode mit einem aussagekräftigen Namen.
Betrachten wir ein Beispiel für schlechten Code:
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;}Hier sind die Berechnung von Überstunden, Steuern und Versicherung vermischt. Trennen wir sie:
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;}Jetzt ist jede Methode für eine Aufgabe verantwortlich. Der Code ist lesbarer und einfacher einzeln zu testen.
3. Entfernen von Duplikaten (DRY)
Legacy-Code leidet oft unter Copy-Paste. Das gleiche Logikstück kann in verschiedenen Klassen vorkommen. Das ist gefährlich: Bei sich ändernden Anforderungen könnten Sie vergessen, eines der Duplikate zu korrigieren.
Angenommen, in zwei verschiedenen Servicen