Refactoring von Legacy-Code in Java: Wo anfangen und wie man das Projekt nicht kaputt macht
Jeder Java-Entwickler stößt früher oder später auf Legacy-Code. Das kann eine monolithische Anwendung sein, die vor zehn Jahren geschrieben wurde, oder ein Projekt mit veralteten Mustern, fehlenden Tests und „magischen Zahlen“. Das Refactoring solchen Codes ist keine leichte Aufgabe, aber absolut notwendig, um die Lebensfähigkeit des Systems zu erhalten.
In diesem Artikel werden wir die wichtigsten Strategien zum Refactoring von Legacy-Code in Java durchgehen, konkrete Beispiele zeigen und praktische Tipps geben, die Ihnen helfen, den Code zu verbessern, ohne die Produktion zu gefährden.
Warum Legacy-Code refactoring benötigt
Legacy-Code ist nicht einfach „alter Code“. Es ist Code, der:
- Schwer zu warten und zu erweitern ist
- Keine ausreichende Testabdeckung hat
- Duplikate und „Code Smells“ enthält
- Veraltete Bibliotheken und Ansätze verwendet
Refactoring verbessert die Lesbarkeit, Leistung und Testbarkeit des Codes. Die Hauptregel ist jedoch: Das externe Verhalten des Systems nicht ändern. Jede Änderung muss sicher und kontrollierbar sein.
Strategien zum Refactoring von Legacy-Code in Java
1. Erstellen eines Schutznetzes aus Tests
Bevor Sie den Code anfassen, schreiben Sie Tests. Das kann schwierig sein, wenn der Code schlecht testbar ist. Verwenden Sie den Ansatz der Characterization Tests (Tests basierend auf dem aktuellen Verhalten).
Beispiel: Sie haben eine Methode, die Bestellungen verarbeitet:
public class OrderProcessor { public double calculateTotal(Order order) { double total = 0; for (Item item : order.getItems()) { if (item.getPrice() > 100) { total += item.getPrice() * 0.9; // 10% Rabatt } else { total += item.getPrice(); } } if (order.isPremium()) { total *= 0.95; // zusätzlicher 5% Rabatt } return total; }}Wir schreiben einen Test, der das aktuelle Verhalten festhält:
@Testpublic void testCalculateTotal_WithPremiumAndExpensiveItems() { Order order = new Order(); order.addItem(new Item("Laptop", 1000)); order.addItem(new Item("Mouse", 50)); order.setPremium(true); double expected = (1000 * 0.9 + 50) * 0.95; // 902.5 assertEquals(expected, new OrderProcessor().calculateTotal(order), 0.001);}Jetzt haben Sie ein „Schutznetz“. Sie können refactoren, ohne Angst zu haben, die Logik zu ändern.
2. Aufteilung großer Methoden (Extract Method)
Eines der häufigsten Probleme von Legacy-Code sind „Monster“-Methoden mit Hunderten von Zeilen. Teilen Sie sie in kleine, gut benannte Methoden auf.
Vor dem Refactoring:
public void processInvoice(Invoice invoice) { // 50 Zeilen Prüfung if (invoice.getCustomer() == null) throw new IllegalArgumentException(); if (invoice.getItems().isEmpty()) throw new IllegalStateException(); // ... weitere 20 Zeilen Prüfungen // 100 Zeilen Berechnung von Rabatten und Steuern // 80 Zeilen E-Mail-Versand und Datenbankspeicherung}Nach dem Refactoring:
public void processInvoice(Invoice invoice) { validateInvoice(invoice); double total = calculateTotal(invoice); saveInvoice(invoice, total); sendConfirmationEmail(invoice);}
private void validateInvoice(Invoice invoice) { if (invoice.getCustomer() == null) throw new IllegalArgumentException(); if (invoice.getItems().isEmpty()) throw new IllegalStateException();}
private double calculateTotal(Invoice invoice) { // Berechnungslogik return invoice.getItems().stream() .mapToDouble(Item::getPrice) .sum();}
private void saveInvoice(Invoice invoice, double total) { // Speicherung}
private void sendConfirma