Entwurfsmuster in C# für Fortgeschrittene: Von der Theorie zum Code
\n\nEntwurfsmuster sind nicht nur abstrakte Konzepte aus Lehrbüchern. Für einen erfahrenen C#-Entwickler werden sie zu einem Werkzeugkasten, der es ermöglicht, flexible, erweiterbare und wartbare Anwendungsarchitekturen zu erstellen. In diesem Artikel analysieren wir drei leistungsstarke Muster, die oft über den Rahmen von Grundkursen hinausgehen: Visitor, Strategy und Command. Sie sehen nicht nur deren Implementierung in C#, sondern verstehen auch, wie sie reale Probleme in der .NET-Ökosystem lösen.
\n\n1. Visitor-Muster (Besucher) – Arbeiten mit Objekthierarchien
\n\nDas Visitor-Muster ermöglicht es, neue Operationen zu bestehenden Klassen hinzuzufügen, ohne deren Struktur zu ändern. Dies ist besonders nützlich, wenn Sie eine komplexe Objekthierarchie haben und verschiedene Aktionen basierend auf deren Typ ausführen möchten.
\n\nProblem
\nStellen Sie sich vor, Sie haben ein Dokumentenverwaltungssystem mit den Klassen TextDocument, Spreadsheet und Presentation. Sie müssen die Funktionalität zum Exportieren in PDF, HTML und XML hinzufügen. Ohne das Visitor-Muster müssten Sie jede Klasse modifizieren, was das Open/Closed-Prinzip (OCP) verletzt.
Implementierung in C#
\n// 1. Element-Schnittstelle\npublic interface IElement\n{\n void Accept(IVisitor visitor);\n}\n\n// 2. Konkrete Elemente\npublic class TextDocument : IElement\n{\n public string Content { get; set; }\n public void Accept(IVisitor visitor) => visitor.Visit(this);\n}\n\npublic class Spreadsheet : IElement\n{\n public List<List<string>> Rows { get; set; }\n public void Accept(IVisitor visitor) => visitor.Visit(this);\n}\n\n// 3. Besucher-Schnittstelle\npublic interface IVisitor\n{\n void Visit(TextDocument doc);\n void Visit(Spreadsheet sheet);\n}\n\n// 4. Konkreter Besucher – Export nach HTML\npublic class HtmlExportVisitor : IVisitor\n{\n public void Visit(TextDocument doc)\n {\n Console.WriteLine($"<h1>{doc.Content}</h1>");\n }\n\n public void Visit(Spreadsheet sheet)\n {\n Console.WriteLine("<table>");\n foreach (var row in sheet.Rows)\n {\n Console.WriteLine("<tr><td>" + string.Join("</td><td>", row) + "</td></tr>");\n }\n Console.WriteLine("</table>");\n }\n}\n\n// Verwendung\nvar docs = new List<IElement> { new TextDocument { Content = "Hallo" }, new Spreadsheet { Rows = new List<List<string>> { new List<string> { "A", "B" } } } };\nvar visitor = new HtmlExportVisitor();\nforeach (var doc in docs) doc.Accept(visitor);\n\nVorteile: Neue Operationen können einfach hinzugefügt werden, ohne bestehende Klassen zu ändern. Nachteile: Wenn sich die Elementhierarchie häufig ändert, wird das Visitor-Muster unpraktisch.
\n\n2. Strategy-Muster (Strategie) – Dynamischer Algorithmenwechsel
\n\nDas Strategy-Muster ermöglicht es, eine Familie von Algorithmen zu definieren, jeden zu kapseln und sie austauschbar zu machen. Dies ist eine klassische Methode, um riesige if-else- oder switch-Konstrukte zu vermeiden.
Problem
\nSie müssen ein System zur Berechnung der Versandkosten implementieren. Die Versandarten sind unterschiedlich: Kurier, Post, Selbstabholung. Jede Art hat ihre eigene Logik zur Preisberechnung.
\n\nImplementierung in C#
\n// 1. Strategie-Schnittstelle\npublic interface IShippingStrategy\n{\n decimal CalculateCost(Order order);\n}\n\n// 2. Konkrete Strategien\npublic class CourierShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order order) => order.Weight * 5.0m + 100;\n}\n\npublic class PostShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order order) => order.Weight * 2.0m + 50;\n}\n\npublic class PickupShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order