Einführung in die SOLID-Prinzipien in C#
\nDie Entwicklung mit C# im .NET-Ökosystem erfordert nicht nur Kenntnisse der Syntax, sondern auch die Fähigkeit, flexible, wartbare und testbare Systeme zu entwerfen. Genau dafür wurden die SOLID-Prinzipien entwickelt – fünf grundlegende Regeln der objektorientierten Programmierung, formuliert von Robert C. Martin (Uncle Bob).
\nDas Akronym SOLID steht für:
\n- \n
- S – Single Responsibility Principle (Prinzip der einzigen Verantwortung) \n
- O – Open/Closed Principle (Prinzip der Offenheit/Geschlossenheit) \n
- L – Liskov Substitution Principle (Liskovsches Substitutionsprinzip) \n
- I – Interface Segregation Principle (Prinzip der Schnittstellentrennung) \n
- D – Dependency Inversion Principle (Prinzip der Abhängigkeitsumkehr) \n
In diesem Artikel werden wir jedes Prinzip praktisch mit Beispielen in C# durchgehen, zeigen, wie deren Verletzung zu Problemen führt und wie die korrekte Anwendung den Code sauberer macht.
\n\n1. Single-Responsibility-Prinzip (SRP)
\nKernaussage: Eine Klasse sollte nur einen einzigen Grund für eine Änderung haben. Mit anderen Worten: Jede Klasse ist für eine klar definierte Aufgabe verantwortlich.
\n\nVerletzung des SRP in C#
\nBetrachten wir ein typisches Beispiel – die Klasse OrderService, die zu viel tut:
public class OrderService\n{\n public void ProcessOrder(Order order)\n {\n // 1. Speichern in der DB\n SaveToDatabase(order);\n \n // 2. E-Mail senden\n SendEmail(order.CustomerEmail, "Bestellung aufgegeben");\n \n // 3. Protokollierung\n File.WriteAllText("log.txt", $"Bestellung {order.Id} verarbeitet");\n }\n \n private void SaveToDatabase(Order order) { /* ... */ }\n private void SendEmail(string email, string message) { /* ... */ }\n}\nProblem: Eine Änderung der E-Mail-Logik oder der Protokollierungsmethode führt zu einer Änderung der Klasse OrderService. Dies verletzt das SRP.
Korrekte Implementierung des SRP
\nWir teilen die Verantwortung auf separate Dienste auf:
\npublic class OrderProcessor\n{\n private readonly IOrderRepository _repository;\n private readonly INotificationService _notificationService;\n private readonly ILogger _logger;\n \n public OrderProcessor(\n IOrderRepository repository,\n INotificationService notificationService,\n ILogger logger)\n {\n _repository = repository;\n _notificationService = notificationService;\n _logger = logger;\n }\n \n public void ProcessOrder(Order order)\n {\n _repository.Save(order);\n _notificationService.SendConfirmation(order.CustomerEmail);\n _logger.Log($"Bestellung {order.Id} verarbeitet");\n }\n}\n\npublic interface IOrderRepository { void Save(Order order); }\npublic interface INotificationService { void SendConfirmation(string email); }\npublic interface ILogger { void Log(string message); }\nJetzt ist jede Klasse nur für ihre eigene Aufgabe verantwortlich. Eine Änderung der Protokollierungslogik hat keine Auswirkungen auf die Auftragsverarbeitung.
\n\n2. Open/Closed-Prinzip (OCP)
\nKernaussage: Klassen sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein. Das bedeutet, Sie sollten in der Lage sein, neues Verhalten hinzuzufügen, ohne vorhandenen Code zu ändern.
\n\nVerletzung des OCP in C#
\nEin klassisches Beispiel – ein Rechner mit Bedingungen:
\npublic class AreaCalculator\n{\n public double CalculateArea(object shape)\n {\n if (shape is Rectangle rect)\n return rect.Width * rect.Height;\n else if (shape is Circle circle)\n return Math.PI * circle.Radius * circle.Radius;\n // Das Hinzufügen einer neuen Form erfordert eine Änderung dieser Methode!\n throw new NotSupportedException();\n }\n}