Fachlicher Vertrag: unverändert zu erhalten
- Preisresultate bleiben für alle CustomerTypes identisch.
- Neue Typen werden ohne wachsenden Switch im Service ergänzt.
- Unsupported Types liefern definierten Fehler.
Switch-basierte Preislogik wird nachvollziehbar in Strategy, Factory und Value Objects überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.
| Dimension | Legacy | Modern | Migrationskonsequenz |
|---|---|---|---|
| Programmiermodell | imperativ / ohne Framework-Annotationen | FunctionalInterface | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 1 direkte Dependencies | 1 direkte Dependencies | 0 neu, 0 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 1 erkannte Methoden | 2 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class LegacyPriceCalculator | class PriceService, class PricingStrategyFactory, enum CustomerType, interface PricingStrategy | Feldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet. |
| Fehlerverhalten | Legacy-Exceptions und Rückgabewerte | explizitere Fach-/Framework-Fehlerabbildung | Fehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden. |
| Tests | Bestands- und Golden-Master-Tests | Unit-, Slice-, Contract- und Integrationstests | Der Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt. |
| Betrieb | Legacy-Start/Lifecycle | modernes Packaging, Health und externe Konfiguration | Golden Master und Vollständigkeitstests verhindern unbemerkte Branch-Lücken. |
| Rollback | Legacy-Artefakt bleibt unverändert | Modern-Artefakt getrennt deploybar | Kein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein. |
branch growth and unsupported customer types
all strategies, factory completeness and unknown type tests
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| LegacyPriceCalculator.java | PriceService.java + PricingStrategy.java + PricingStrategyFactory.java | Switch wird Strategy/Factory-Komposition. |
| String/Branch Customer Type | CustomerType.java | Kundentyp wird expliziter Value/Enum-Vertrag. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| LegacyPriceCalculator.java | class LegacyPriceCalculator | — | double price(String customerType, double base) | Conditional Strategy (Legacy-Antipattern) – wählt Preisalgorithmen über Bedingungen. | 11 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| CustomerType.java | enum CustomerType | — | — | — | 5 |
| PriceService.java | class PriceService | — | double price(CustomerType type, double base) | — | 12 |
| PricingStrategy.java | interface PricingStrategy | FunctionalInterface | — | Strategy | 8 |
| PricingStrategyFactory.java | class PricingStrategyFactory | — | PricingStrategy forType(CustomerType type) | Factory | 13 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| BEIBEHALTEN | org.junit.jupiter:junit-jupiter | BOM/Parent → BOM/Parent | test → test | Gemeinsame Dependency; Version und Scope im effektiven POM prüfen. |
Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.
Migrationsbedeutung: Switch wird Strategy/Factory-Komposition.
package at.aydin.lab.patterns.legacy;
// Design Pattern: Conditional Strategy (Legacy-Antipattern) – wählt Preisalgorithmen über Bedingungen.
public final class LegacyPriceCalculator {
public double price(String customerType, double base) {
if ("STANDARD".equals(customerType)) return base;
if ("PREMIUM".equals(customerType)) return base * 0.9;
if ("EMPLOYEE".equals(customerType)) return base * 0.7;
throw new IllegalArgumentException(customerType);
}
}
projects/34-patterns-refactoring/modern/src/main/java/at/aydin/lab/patterns/modern/PriceService.java
package at.aydin.lab.patterns.modern;
public final class PriceService {
private final PricingStrategyFactory factory;
public PriceService(PricingStrategyFactory factory) {
this.factory = factory;
}
public double price(CustomerType type, double base) {
return factory.forType(type).apply(base);
}
}
package at.aydin.lab.patterns.modern;
// Design Pattern: Strategy
// Zweck: Rabattalgorithmus ist austauschbar und ohne zentrale Switch-Anweisung erweiterbar.
@FunctionalInterface
public interface PricingStrategy {
double apply(double basePrice);
}
Migrationsbedeutung: Kundentyp wird expliziter Value/Enum-Vertrag.
package at.aydin.lab.patterns.legacy;
// Design Pattern: Conditional Strategy (Legacy-Antipattern) – wählt Preisalgorithmen über Bedingungen.
public final class LegacyPriceCalculator {
public double price(String customerType, double base) {
if ("STANDARD".equals(customerType)) return base;
if ("PREMIUM".equals(customerType)) return base * 0.9;
if ("EMPLOYEE".equals(customerType)) return base * 0.7;
throw new IllegalArgumentException(customerType);
}
}
projects/34-patterns-refactoring/modern/src/main/java/at/aydin/lab/patterns/modern/CustomerType.java
package at.aydin.lab.patterns.modern;
public enum CustomerType {
STANDARD, PREMIUM, EMPLOYEE
}
--- LegacyPriceCalculator.java
+++ CustomerType.java
@@ -1,11 +1,5 @@
-package at.aydin.lab.patterns.legacy;
+package at.aydin.lab.patterns.modern;
-// Design Pattern: Conditional Strategy (Legacy-Antipattern) – wählt Preisalgorithmen über Bedingungen.
-public final class LegacyPriceCalculator {
- public double price(String customerType, double base) {
- if ("STANDARD".equals(customerType)) return base;
- if ("PREMIUM".equals(customerType)) return base * 0.9;
- if ("EMPLOYEE".equals(customerType)) return base * 0.7;
- throw new IllegalArgumentException(customerType);
- }
+public enum CustomerType {
+ STANDARD, PREMIUM, EMPLOYEE
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 34-PATTERNS-REFACTORING-A01 | Integration/Contract | Alle bisherigen Preisfälle sind identisch. | Automatisierter Test und CI-Protokoll |
| 34-PATTERNS-REFACTORING-A02 | Integration/Contract | Jeder Enum-Wert hat genau eine Strategie. | Automatisierter Test und CI-Protokoll |
| 34-PATTERNS-REFACTORING-A03 | Integration/Contract | Unbekannter/null Typ scheitert verständlich. | Automatisierter Test und CI-Protokoll |
| 34-PATTERNS-REFACTORING-A04 | Integration/Contract | Neue Strategie erfordert keine Änderung bestehender Strategien. | Automatisierter Test und CI-Protokoll |
| 34-PATTERNS-REFACTORING-A05 | Integration/Contract | Factory-Konfiguration ist unit-getestet. | Automatisierter Test und CI-Protokoll |
| 34-PATTERNS-REFACTORING-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 34-PATTERNS-REFACTORING-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 34-PATTERNS-REFACTORING-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 34-PATTERNS-REFACTORING-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 34-PATTERNS-REFACTORING-F01 | Fokus | all strategies, factory completeness and unknown type tests | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| branch growth and unsupported customer types | NIEDRIG | all strategies, factory completeness and unknown type tests | vor Cutover |
Keine zusätzlichen Ressourcen.
PriceService kann vorübergehend eine LegacyStrategy verwenden; der äußere Servicevertrag bleibt stabil.