SOLID-Prinzipien in Java: Von der Theorie zur fortgeschrittenen Praxis
Die SOLID-Prinzipien sind das Fundament des objektorientierten Designs, das Entwicklern hilft, Code zu erstellen, der änderungsresistent, leicht testbar und skalierbar ist. Für einen Java-Entwickler bedeutet das Verständnis dieser Prinzipien auf fortgeschrittenem Niveau nicht nur die Kenntnis der Definitionen, sondern die Fähigkeit, ihre Verletzung in Legacy-Code zu erkennen und Muster für das Refactoring anzuwenden. In diesem Artikel werden wir jedes Prinzip mit Schwerpunkt auf komplexe, aber typische Situationen analysieren.
1. Single Responsibility Principle (SRP): Die Grenze zwischen Verantwortung und Kohäsion
Theorie: Eine Klasse sollte nur einen Grund für Änderungen haben. In der Praxis bedeutet dies, dass eine Klasse für einen Teil der Funktionalität des Programms verantwortlich sein sollte.
Fortgeschrittene Praxis: Oft zeigt sich eine SRP-Verletzung nicht in einer offensichtlichen Vermischung von Logik, sondern in einer "verschwommenen" Verantwortung. Betrachten wir ein Beispiel einer Klasse für die Arbeit mit Berichten:
// SRP-Verletzung: Die Klasse kümmert sich um Daten, Formatierung und Versandpublic class ReportService { public void generateReport(List<Data> data) { // Berechnungslogik Map<String, Integer> aggregated = aggregate(data); // Formatierung (HTML) String html = formatToHtml(aggregated); // Versand per E-Mail sendEmail(html, "admin@example.com"); }
private Map<String, Integer> aggregate(List<Data> data) { // ... return new HashMap<>(); }
private String formatToHtml(Map<String, Integer> data) { // ... return "<html>...</html>"; }
private void sendEmail(String content, String to) { // ... }}Problem: Wenn sich das Berichtsformat ändert (PDF statt HTML), muss diese Klasse geändert werden. Wenn sich die Versandart ändert (Telegram statt E-Mail) – ebenfalls. Lösung: Aufteilung in drei Klassen:
public class ReportAggregator { public Map<String, Integer> aggregate(List<Data> data) { ... }}
public class HtmlReportFormatter { public String format(Map<String, Integer> data) { ... }}
public class EmailSender { public void send(String content, String to) { ... }}
// Komposition im Servicepublic class ReportGenerationFacade { private ReportAggregator aggregator; private HtmlReportFormatter formatter; private EmailSender sender;
public void generateAndSend(List<Data> data) { var aggregated = aggregator.aggregate(data); var html = formatter.format(aggregated); sender.send(html, "admin@example.com"); }}2. Open-Closed Principle (OCP): Die Kunst der Erweiterung durch Abstraktionen
Theorie: Klassen sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein.
Fortgeschrittene Praxis: Eine typische Verletzung sind if-else- oder switch-Ketten für verschiedene Objekttypen. Die fortgeschrittene Methode ist die Verwendung von Strategie oder Template Method mit Polymorphismus.
// OCP-Verletzung: Beim Hinzufügen eines neuen Zahlungstyps muss die Klasse geändert werdenpublic class PaymentProcessor { public void processPayment(String type, double amount) { if ("CREDIT_CARD".equals(type)) { // Logik für Kreditkarte } else if ("PAYPAL".equals(type)) { // Logik für PayPal } else if ("BITCOIN".equals(type)) { // Logik für Bitcoin } }}Korrektur über Interface und Factory:
public interface PaymentMethod { void pay(double amount);}
public class CreditCardPayment implements PaymentMethod { public void pay(double amount) { ... }}
public class PayPalPayment implements PaymentMethod