Entwurfsmuster in C# mit Beispielen: von A bis Z
Entwurfsmuster (Design Patterns) sind bewährte Lösungen für typische Probleme, die bei der Softwareentwicklung auftreten. Sie sind kein fertiger Code, sondern Vorlagen, die an die jeweilige Situation angepasst werden können. Für einen C#-Entwickler ist die Kenntnis von Entwurfsmustern nicht nur ein Plus im Lebenslauf, sondern eine Notwendigkeit für die Erstellung einer flexiblen, wartbaren und skalierbaren Architektur.
In diesem Artikel werden wir die drei Hauptgruppen von Entwurfsmustern nach der GoF-Klassifikation (Gang of Four) behandeln: Erzeugungsmuster, Strukturmuster und Verhaltensmuster. Jedes Muster wird anhand eines praktischen Beispiels in C# (.NET) betrachtet.
Erzeugungsmuster (Creational Patterns)
Diese Muster steuern den Prozess der Objekterstellung und machen das System unabhängig von der Art und Weise ihrer Erzeugung. Betrachten wir zwei der beliebtesten.
Singleton (Einzelstück)
Stellt sicher, dass eine Klasse nur eine Instanz hat, und bietet einen globalen Zugriffspunkt darauf. In C# wird für sicheres Arbeiten in einer Multithread-Umgebung Lazy<T> verwendet.
public sealed class Logger{ private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
public void Log(string message) { Console.WriteLine($"[{DateTime.Now}] {message}"); }}
// VerwendungLogger.Instance.Log("Anwendung gestartet");Factory Method (Fabrikmethode)
Definiert eine Schnittstelle zur Erstellung eines Objekts, ermöglicht es Unterklassen jedoch, den Typ des erstellten Objekts zu ändern. Ideal für Situationen, in denen der Objekttyp im Voraus nicht bekannt ist.
public interface ITransport{ void Deliver();}
public class Truck : ITransport{ public void Deliver() => Console.WriteLine("Lieferung auf dem Landweg");}
public class Ship : ITransport{ public void Deliver() => Console.WriteLine("Lieferung auf dem Seeweg");}
public abstract class Logistics{ public abstract ITransport CreateTransport();
public void PlanDelivery() { var transport = CreateTransport(); transport.Deliver(); }}
public class RoadLogistics : Logistics{ public override ITransport CreateTransport() => new Truck();}
// VerwendungLogistics logistics = new RoadLogistics();logistics.PlanDelivery(); // Ausgabe: Lieferung auf dem LandwegStrukturmuster (Structural Patterns)
Sie definieren Möglichkeiten zum Aufbau von Beziehungen zwischen Objekten und sorgen so für Flexibilität und Effizienz der Architektur.
Adapter
Ermöglicht Objekten mit inkompatiblen Schnittstellen die Zusammenarbeit. Wird häufig bei der Integration von Altcode oder Bibliotheken von Drittanbietern verwendet.
// Ziel-Schnittstelle, die vom Client erwartet wirdpublic interface IModernPrinter{ void Print(string text);}
// Alte Klasse mit inkompatibler Schnittstellepublic class OldPrinter{ public void PrintOldStyle(string message) { Console.WriteLine($"Alter Drucker: {message}"); }}
// Adapterpublic class PrinterAdapter : IModernPrinter{ private readonly OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter) { _oldPrinter = oldPrinter; }
public void Print(string text) { _oldPrinter.PrintOldStyle(text); }}
// VerwendungIModernPrinter printer = new PrinterAdapter(new OldPrinter());printer.Print("Hallo, Welt!"); // Ausgabe: Alter Drucker: Hallo, Welt!Decorator (Dekorierer)
Fügt einem Objekt dynamisch neue Aufgaben hinzu, ohne dessen Code zu ändern. Es ist eine flexible Alternative zur Vererbung.
public interface ICoffee{ double GetCost(); string GetDescription();}
public class SimpleCoffee : ICoffee{