Entwurfsmuster (Softwareentwicklung)
Entwurfsmuster in der Softwareentwicklung
Entwurfsmuster (engl. *Design Patterns*) sind wiederverwendbare Lösungsansätze für häufig auftretende Probleme in der Softwareentwicklung. Sie beschreiben bewährte Strukturen und Interaktionen von Klassen und Objekten, ohne eine konkrete Implementierung vorzugeben.
Ziel
Das Ziel von Entwurfsmustern ist es, Software:
- **verständlicher**,
- **wartbarer**,
- **erweiterbarer**
und **wiederverwendbarer** zu machen.
Sie fördern eine saubere Architektur, klare Verantwortlichkeiten und vermeiden redundanten Code.
Ursprung
Das Konzept der Entwurfsmuster wurde durch die sogenannte „Gang of Four“ (GoF) geprägt:
- Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides* veröffentlichten 1994 das Buch *„Design Patterns: Elements of Reusable Object-Oriented Software“*, das 23 grundlegende Muster definiert.
Klassifikation
Die GoF-Entwurfsmuster werden in drei Hauptkategorien eingeteilt:
1. Erzeugungsmuster (Creational Patterns)
Diese Muster befassen sich mit der Erzeugung von Objekten und trennen die Objektinstanziierung von der Nutzung.
- Singleton: Stellt sicher, dass es nur eine Instanz einer Klasse gibt.
- Factory Method: Delegiert die Objekterzeugung an Unterklassen.
- Abstract Factory: Erzeugt Familien verwandter Objekte, ohne deren konkrete Klassen anzugeben.
- Builder: Trennt die Konstruktion komplexer Objekte von ihrer Darstellung.
- Prototype: Erstellt neue Objekte durch Kopieren eines vorhandenen Prototyps.
2. Strukturmuster (Structural Patterns)
Diese Muster beschreiben, wie Klassen und Objekte zu größeren Strukturen zusammengesetzt werden.
- Adapter: Passt inkompatible Schnittstellen aneinander an.
- Bridge: Trennt Abstraktion und Implementierung, sodass beide unabhängig voneinander variieren können.
- Composite: Ermöglicht die einheitliche Behandlung einzelner Objekte und Objektgruppen.
- Decorator: Fügt Objekten dynamisch zusätzliche Funktionalität hinzu.
- Facade: Bietet eine vereinfachte Schnittstelle zu einem komplexen Subsystem.
- Flyweight: Spart Speicher, indem häufig wiederkehrende Objekte geteilt werden.
- Proxy: Stellt ein Stellvertreterobjekt für ein anderes Objekt bereit.
3. Verhaltensmuster (Behavioral Patterns)
Diese Muster befassen sich mit der Kommunikation und Zusammenarbeit zwischen Objekten.
- Chain of Responsibility: Leitet Anfragen entlang einer Kette von Objekten weiter.
- Command: Kapselt Befehle als Objekte.
- Iterator: Ermöglicht den sequenziellen Zugriff auf Elemente einer Sammlung.
- Mediator: Kapselt die Interaktion mehrerer Objekte in einem Vermittlerobjekt.
- Memento: Erlaubt das Speichern und Wiederherstellen von Objektzuständen.
- Observer: Benachrichtigt abhängige Objekte automatisch bei Zustandsänderungen.
- State: Kapselt Zustandsabhängiges Verhalten in separate Klassen.
- Strategy: Definiert austauschbare Algorithmen über gemeinsame Schnittstellen.
- Template Method: Definiert ein Grundgerüst eines Algorithmus, dessen Schritte Unterklassen spezifizieren können.
- Visitor: Trennt Operationen von den Objekten, auf denen sie ausgeführt werden.
Vorteile
- Förderung von **Modularität** und **Wiederverwendbarkeit**
- Erleichtert **Kommunikation** durch standardisierte Begriffe
- Erhöht **Flexibilität** und **Erweiterbarkeit**
- Verbessert **Wartbarkeit** und **Testbarkeit**
Nachteile
- Erhöhter **Abstraktionsgrad** und damit höhere **Komplexität**
- Gefahr von **Überengineering**, wenn Muster unnötig eingesetzt werden
- Kann **Leistungseinbußen** verursachen (z. B. durch zusätzliche Objektschichten)
Beispiel: Singleton-Muster in Java
public class Singleton {
private static Singleton instance;
private Singleton() {
// privater Konstruktor verhindert externe Instanziierung
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void printMessage() {
System.out.println("Singleton-Instanz aktiv");
}
}
Beispiel: Observer-Muster in Python
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def notify(self, message):
for obs in self._observers:
obs.update(message)
class Observer:
def update(self, message):
print(f"Observer erhielt Nachricht: {message}")
subject = Subject()
o1 = Observer()
o2 = Observer()
subject.attach(o1)
subject.attach(o2)
subject.notify("Zustandsänderung erfolgt!")
Anwendung in modernen Architekturen
Entwurfsmuster werden auch in modernen Softwarearchitekturen eingesetzt:
- **MVC, MVP, MVVM** – zur Trennung von Darstellung und Logik
- **Dependency Injection (DI)** – zur Entkopplung von Komponenten
- **Event Sourcing** und **CQRS** – in verteilten Systemen und Microservices
- **Repository Pattern** – in Datenzugriffsschichten
Zusammenhang mit Softwarearchitektur
Entwurfsmuster sind Bausteine für größere Architekturkonzepte. Während Architekturmuster (z. B. Microservices-Architektur, Client-Server-Architektur) die Gesamtstruktur einer Anwendung beschreiben, lösen Entwurfsmuster Probleme auf Klassen- oder Objektebene.
Bekannte Erweiterungen
- **Enterprise Patterns** (z. B. in der Java EE-Welt: Service Locator, DAO, Session Facade)
- **Concurrency Patterns** (z. B. Producer-Consumer, Thread Pool, Reactor Pattern)
- **Functional Patterns** (z. B. Monaden, Funktoren in funktionalen Sprachen)
Werkzeuge und Frameworks
Viele Frameworks implementieren Entwurfsmuster intern:
- **Spring Framework (Java):** Dependency Injection, Proxy, Singleton
- **Angular (JavaScript):** Observer, Dependency Injection
- **.NET Framework:** Factory, Strategy, Decorator
Siehe auch
Literatur
- Gamma, Helm, Johnson, Vlissides: Design Patterns – Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994.
- Buschmann et al.: Pattern-Oriented Software Architecture, Wiley, 1996.
- Freeman, Freeman: Head First Design Patterns, O’Reilly, 2004.