Design Patterns sind ein Vokabular, nicht Rezepte.
Beginne mit dem Problem und entscheide dann, ob ein Muster die Komplexität reduziert, ohne dass es zu einem zusätzlichen Aufwand wird.
Drei grobe Kategorien
- Erzeugung: Die Erstellung von Objekten ist komplex oder variabel.
- Struktur: Beziehungen zwischen Objekten müssen organisiert werden.
- Verhalten: Kommunikation oder Algorithmen müssen variabel sein.
Erzeugungsmuster
Factory Method
Verwende sie, wenn ein Erzeuger eine konkrete Implementierung auswählen soll, ohne Konstruktionsdetails offenzulegen.
Diese Methode ermöglicht es, flexibel die zu erstellenden Objekte zu wählen, ohne dass der Verbraucher die Details ihres Aufbaus kennen muss.
So lässt sich später ein anderer konkreter Typ einführen, ohne dass der Aufrufer angepasst werden muss.
Abstract Factory
Verwende sie für Familien zusammengehöriger Objekte, die kompatibel sein müssen, etwa passende UI‑Steuerelemente.
Dies sorgt dafür, dass alle Komponenten gut zusammenarbeiten und Konsistenz im System gewährleistet ist.
Weil die Fabrik nur abstrakte Schnittstellen zurückgibt, bleibt das gesamte Set von Komponenten austauschbar, wenn man etwa von einem Windows‑ zu einem macOS‑Look wechselt.
Builder
Verwende ihn bei vielen optionalen Parametern oder Konstruktionsschritten.
Dies erlaubt die Schritt‑für‑Schritt‑Konstruktion komplexer Objekte, wodurch es einfacher wird, verschiedene Konfigurationen zu verwalten, ohne Code zu duplizieren.
Der Builder sammelt die Eingaben, erst beim Aufruf von build entsteht das fertige Objekt, was die Lesbarkeit des Aufrufercodes erhöht.
Singleton
Verwende ihn nur, wenn eine Instanz eine echte Invariante ist.
Dependency Injection ist oft einfacher zu testen und zu verstehen, was es zu einer besseren Wahl macht, wenn das Singleton‑Muster nicht unbedingt notwendig ist.
Ein globaler Logger etwa ist ein typisches Beispiel; andernfalls führt ein Singleton leicht zu versteckten Abhängigkeiten, die das Testen erschweren.
Strukturmuster
Adapter
Verwende ihn, wenn eine vorhandene Schnittstelle nicht zu der von deinem Code erwarteten passt.
Mit diesem Muster kannst du inkompatible Schnittstellen verbinden und den Abstand zwischen ihnen überbrücken, was es ermöglicht, vorhandene Klassen zu verwenden, ohne sie zu ändern.
Der Adapter implementiert die erwartete API und leitet die Aufrufe an die alte Klasse weiter, wobei er bei Bedarf Parameter umwandelt.
Decorator
Verwende ihn, um Verhalten um ein Objekt herum zu ergänzen, ohne dessen Klasse zu ändern.
Dies ermöglicht die Erweiterung der Funktionalitäten eines Objekts während der Laufzeit, ohne dessen Kernstruktur zu verändern.
Jeder Decorator hält eine Referenz auf das zu dekorierende Objekt und kann vor oder nach dem Aufruf zusätzliche Aktionen ausführen, etwa Logging oder Kompression.
Facade
Verwende sie, um eine fokussierte Schnittstelle zu einem komplexen Subsystem anzubieten.
Diese Muster vereinfacht die Nutzung komplexer Subsysteme, indem es eine einfache, vereinte Schnittstelle bietet, die die zugrunde liegende Komplexität verbirgt.
Die Fassade kapselt mehrere Klassen hinter einer Methode, sodass Änderungen im Subsystem nur die Fassade betreffen und der Aufrufer unverändert bleibt.
Composite
Verwende es, wenn einzelne Objekte und Gruppen dieselben Operationen unterstützen sollen, etwa Dateien und Ordner.
Mit diesem Muster lassen sich einzelne Objekte und Kompositionen von Objekten gleich behandeln, was es einfacher macht, hierarchische Strukturen zu verwalten.
Dadurch kann ein Algorithmus rekursiv über das gesamte Baumwerk laufen, ohne zwischen Blatt und Knoten unterscheiden zu müssen.
Verhaltensmuster
Observer
Verwende ihn, wenn ein Subjekt mehrere Listener über Änderungen informieren soll, ohne sie fest einzubauen.
Dieses Muster gewährleistet, dass Änderungen an einem Zustand den Interessenten übermittelt werden, ohne dass sie enge Kopplungen unterliegen, was eine lose Kopplung und Flexibilität fördert.
Der Beobachter registriert sich beim Subjekt, das anschließend bei jeder Zustandsänderung die Observer benachrichtigt.
Command
Verwende es für Aktionen mit Historie, Warteschlangen, Makros, Audit oder Undo.
Dies ermöglicht es, Aktionen in Objekte zu kapseln, was die Verwaltung von Aktionen, die eine Historie haben, vereinfacht.
Jedes Command trägt alle Parameter und die Logik zur Ausführung, sodass es später wiederholt oder rückgängig gemacht werden kann.
Strategy
Verwende sie, wenn ein Algorithmus zur Laufzeit ausgewählt oder ersetzt werden soll.
Dieses Muster erlaubt es, den Algorithmus, der von einer Klasse verwendet wird, zur Laufzeit auszuwählen, was das Umschalten zwischen verschiedenen Verhaltensweisen ohne Änderung der Klasse ermöglicht.
Die Kontextklasse hält nur eine Referenz auf das Strategie‑Interface; ein Austausch der konkreten Strategie ändert das Verhalten sofort.
Mediator
Verwende ihn, wenn direkte Kommunikation zwischen vielen Objekten unübersichtlich geworden ist.
Dieses Muster führt ein Mediator‑Objekt ein, das die Kommunikation zwischen einer Reihe von Objekten kapselt, was die Kommunikation zwischen ihnen verwalten und die Komplexität von direkten Interaktionen reduzieren kann.
Statt dass jedes Objekt jeden anderen kennt, sendet es Nachrichten an den Mediator, der dann entscheidet, welche Empfänger informiert werden.
Chain of Responsibility
Verwende sie, wenn eine Anfrage Handler durchlaufen soll, bis einer sie übernimmt.
Dieses Muster ermöglicht es, eine Anfrage entlang einer Kette von möglichen Handlers zu übertragen, bis einer in der Lage ist, die Anfrage zu bearbeiten, was den Anfrageprozess flexibler und entkoppelter macht.
Damit lässt sich die Reihenfolge der Handler ändern oder neue Handler einfügen, ohne den Aufrufer zu berühren.
Kompakte Entscheidungsmatrix
| Problem | Mögliches Muster |
|---|---|
| Unterschiedliche Konstruktion | Factory Method |
| Kompatible Objektfamilien | Abstract Factory |
| Viele Konstruktionsoptionen | Builder |
| Nicht passende Schnittstelle | Adapter |
| Verhalten dynamisch ergänzen | Decorator |
| Subsystem vereinfachen | Facade |
| Baum aus austauschbaren Objekten | Composite |
| Benachrichtigungen an viele | Observer |
| Rückgängige oder wartende Aktionen | Command |
| Auswählbarer Algorithmus | Strategy |
| Verworrene Peer‑Kommunikation | Mediator |
| Erster geeigneter Handler | Chain of Responsibility |
Pattern‑getriebenes Design vermeiden
Patterns bringen Namen, Indirektion und Wartungskosten mit.
Ein Muster ist sinnvoll, wenn das Problem wiederkehrt, das Team den Kompromiss versteht und das Ergebnis leichter zu ändern oder zu testen ist.
Führe kein Muster nur wegen seines Namens ein.
Fazit
Wähle das kleinste Design, das Verantwortlichkeiten klar hält.
Ein Pattern sollte aus den Einschränkungen des Problems entstehen und nicht aus Selbstzweck in eine Codebasis gedrückt werden.
Dies sorgt dafür, dass die Lösung sowohl effektiv als auch wartbar ist.
Wenn sich die Anforderungen verschieben, sollte das Design sich anpassen können, ohne dass ein schwergewichtiges Muster ausgetauscht werden muss.

Comments (0)
Please sign in to leave a comment