Bild von Design-Patterns und Anti-Patterns verstehen

29. Sep 2025

Design-Patterns und Anti-Patterns verstehen

Design-Patterns helfen, komplexe Probleme elegant zu lösen. Anti-Patterns zeigen uns, wie man es besser nicht macht. In unserem Mentoring-Programm haben wir häufig auftretende Patterns zusammengetragen und klären kurz über deren Sinn und Unsinn auf.

Jede Entwicklerin und jeder Entwickler steht früher oder später vor den gleichen Fragen: Wie strukturiere ich meinen Code sinnvoll? Wie halte ich Projekte flexibel, wartbar und erweiterbar? Hier kommen Design-Patterns ins Spiel – bewährte, wiederverwendbare Lösungen für typische Herausforderungen in der Softwareentwicklung. Sie unterstützen gute Architektur, fördern die Teamkommunikation und steigern die Codequalität.

Gleichzeitig gibt es Anti-Patterns: Ansätze, die zwar gut gemeint sind, aber schnell zu schlechter Praxis führen können – und zeigen, wie man diese Fallen vermeidet.

Übergeordnete Prinzipien für gutes Software-Design

Grundsätzlich zeichnet sich gutes Software-Design durch Klarheit, Einfachheit und Wiederverwendbarkeit aus. Die Beachtung von Grundprinzipien helfen dabei, sauberen, verständlichen und wartbaren Code zu entwickeln. Zu beachten sind: SOLID, DRY und KISS.

Designprinzip SOLID

Prinzip: SOLID ist ein Satz von fünf objektorientierten Designprinzipien, der Software so strukturiert, dass sie wartbar, erweiterbar und robust gegenüber Änderungen bleibt. Mehr dazu in unserem News-Artikel .

Designprinzip DRY – «Don't Repeat Yourself»

Prinzip: Jedes Stück Wissen sollte eine einzige, eindeutige Repräsentation im System haben. 

Designprinzip KISS – «Keep It Simple and Stupid» 

Prinzip: Systeme funktionieren am besten, wenn sie einfach gehalten sind. Vermeide unnötige Komplexität.

Klassische Design-Patterns

Klassische Design-Patterns sind bewährte Lösungsansätze für wiederkehrende Probleme in der Softwareentwicklung. Sie dienen als Schablonen, die helfen, Code verständlicher, wartbarer und erweiterbarer zu gestalten.

Sie lassen sich grob in drei Kategorien einteilen:

Creational: Strategien zur Objekterstellung, die Instanziierung kapseln und Abhängigkeiten reduzieren.

Structural: Konzepte zum Zusammenspiel von Klassen und Objekten, um Strukturen flexibel und wartbar zu halten.

Behavioral: Muster für die Zusammenarbeit von Objekten und die Verteilung von Verantwortungen und Algorithmen.

Wir stellen folgende Design-Patterns kurz vor: 

Observer-Pattern

Motivation: Oft müssen mehrere Objekte automatisch informiert werden, wenn sich der Zustand eines anderen Objekts ändert. Möchte man eine harte Kopplung vermeiden, dann bietet sich das Observer-Pattern an, da das beobachtete Objekt sein Empfänger nicht kennen und auch nicht direkt ansprechen muss. 

Beschreibung: Das Observer-Pattern stellt eine Eins-zu-Viele-Abhängigkeit zwischen Objekten her, bei der mehrere «Observer-Objekte» benachrichtigt werden, wenn das «Subjekt» seinen Status ändert. Es wird häufig für die Ereignisbehandlung und die Implementierung von Abonnementmodellen verwendet.

Anwendungsfälle:

  • Benachrichtigungssysteme (z. B. E-Mail, SMS)
  • GUI-Frameworks
  • Messaging-Systeme
UML
Codebeispiel

Pattern der Factory-Methode

Motivation: Die Logik zum Erzeugen von Objekten soll flexibel bleiben, ohne den aufrufenden Code ändern zu müssen. Das Instanziieren eines Objektes wird von der restlichen Logik getrennt. 

Beschreibung: Das Pattern der Factory-Methode definiert eine Schnittstelle zum Erstellen von Objekten, lässt jedoch Unterklassen entscheiden, welche Klasse instanziiert werden soll.

Anwendungsfälle:

  • Dynamisches Verwalten der Objekterstellung
  • Wechseln zwischen verschiedenen Arten von verwandten Objekten
  • Implementierung der Abhängigkeitsinjektion
UML
Codebeispiel

Strategy-Pattern

Motivation: Manchmal gibt es für bestimmte Aufgaben mehrere Lösungsstrategien und man möchte dynamisch entscheiden, welcher Ansatz gewählt werden soll. Anstatt dies im Code fest zu verdrahten, bietet das Strategy-Pattern hier Abhilfe. 

Beschreibung: Das Strategy-Pattern definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie austauschbar. Der Client kann den zu verwendenden Algorithmus dynamisch auswählen, ohne den Code des Clients zu ändern.

Anwendungsfälle:

  • Zahlungsmethoden für eine E-Commerce-Website (z. B. PayPal, Kreditkarte)
  • Sortieralgorithmen
  • Konfiguration des dynamischen Verhaltens
UML
Codebeispiel

Adapter-Pattern

Motivation: Systeme müssen oft mit bereits bestehenden oder fremden Systemen interagieren, die nicht immer zusammenpassen. Ein Adapter übersetzt zwischen diesen Welten. 

Beschreibung: Das Adapter Pattern konvertiert die Schnittstelle einer Klasse in eine andere Schnittstelle, die ein Client erwartet. Es ermöglicht die Zusammenarbeit von ansonsten inkompatiblen Schnittstellen.

Anwendungsfälle:

  • Integration von Legacy-Code in ein neues System
  • Verwenden von Bibliotheken mit inkompatiblen Schnittstellen
  • Überbrückung der Kommunikation zwischen Subsystemen
UML
Codebeispiel

Decorator-Pattern

Motivation: Bestehende Klassen benötigen zusätzliche Funktionalitäten, sollen aber intern nicht verändert werden und auch eine komplexe Vererbungshierarchie ist nicht gewünscht. Unter diesen Bedingungen lohnt es sich, das Decorator-Pattern in Betracht zu ziehen.  

Beschreibung: Das Decorator-Pattern fügt einem Objekt dynamisch zusätzliche Funktionen hinzu. Es ist nützlich, um das Verhalten von Objekten zu erweitern, ohne ihre Struktur zu ändern.

Anwendungsfälle:

  • Erweitern der Funktionalität von UI-Komponenten
  • Dynamisches Hinzufügen von Sicherheitsfunktionen
  • Protokollierung und Überwachung
UML
Codebeispiel

Command-Pattern

Motivation: Aktionen in einem System sind meist sehr flüchtig und nicht einfach nachzuvollziehen. Wenn man unterschiedliche Aktionen als eigene Objekte ansieht, dann bietet dies diverse Vorteile. 

Beschreibung: Das Command-Pattern kapselt eine Anforderung als Objekt und ermöglicht es so, Methoden mit Anforderungen, verzögerter Ausführung oder Warteschlangenanforderungen zu parametrisieren. Es ermöglicht die Funktion «Rückgängig» und «Wiederholen».

Anwendungsfälle:

  • Aufgabenplanung
  • Undo/Redo-Operationen in Texteditoren
  • Fernsteuerungssysteme
UML
Codebeispiel

Builder-Pattern

Motivation: Bei komplexen Objekten mit vielen optionalen Parametern kann die Konstruktion schnell unübersichtlich werden, vor allem wenn mehrere Build-Steps involviert sind, welche Abhängigkeiten zueinander aufweisen. 

Beschreibung: Das Builder-Pattern trennt die Konstruktion eines komplexen Objekts von seiner Darstellung, sodass mit demselben Konstruktionsprozess unterschiedliche Darstellungen erstellt werden können. Es ist nützlich, um Objekte mit vielen optionalen Parametern zu erstellen.

Anwendungsfälle:

  • Erstellen eines Berichts oder Dokuments mit mehreren Abschnitten
  • Konfigurieren von Objekten mit vielen optionalen Parametern
  • Generierung komplexer Produktdarstellungen
UML
Codebeispiel

 

Proxy-Pattern

Motivation: Direkter Zugriff auf ein Objekt ist manchmal nicht sinnvoll oder zu teuer – etwa wenn es geschützt, verzögert geladen oder überwacht werden soll. 

Beschreibung: Das Proxy-Pattern wird verwendet, um den kontrollierten Zugriff auf ein Objekt zu ermöglichen. Dies kann in Szenarien wie Zugriffssteuerung, verzögerter Initialisierung oder Protokollieren von Aufrufen des Objekts nützlich sein.

Anwendungsfälle:

  • Virtueller Proxy: Laden grosser Bilder oder Videos nur bei Bedarf (Lazy Loading)
  • Access Control Proxy: Authentifizierung und Autorisierung vor dem Zugriff auf sensible Ressourcen
  • Caching-Proxy: Speichern von Daten, auf die häufig zugegriffen wird, um die Leistung zu verbessern
  • Remote Proxy: Darstellung von Objekten, die in unterschiedlichen Adressräumen existieren (Webservices, Remote Datenbanken)
  • Protokollierungsproxy: Aufzeichnen von Methodenaufrufen und ZugriffsPatternn für das Debuggen
UML
Codebeispiel

Prototype-Pattern

Motivation: Wenn die Erzeugung neuer Objekte sehr aufwendig oder komplex ist, kann es unter Umständen einfacher sein bereits vorhandene Instanzen zu kopieren.  

Beschreibung: Das Prototype-Pattern wird verwendet, wenn das Erstellen neuer Instanzen einer Klasse teuer ist. Anstatt Objekte von Grund auf neu zu erstellen, wird ein vorhandenes Objekt geklont.

Anwendungsfälle:

  • Spielentwicklung: Klonen von Spielobjekten (Feinde, Power-Ups), anstatt sie von Grund auf neu zu erstellen
  • Datenbankeinträge: Kopieren von Datenbankentitäten mit vorausgefüllten Standardwerten
  • Konfigurationsobjekte: Erstellen von Variationen komplexer Konfigurationseinstellungen
  • Dokumentvorlagen: Kopieren von Dokumentvorlagen mit vordefinierter Formatierung
  • Teure Objekterstellung: Wenn die Objektinitialisierung kostspielige Vorgänge erfordert (Netzwerkaufrufe, Datei-I/O)
UML
Codebeispiel

Mediator-Pattern

Motivation: Viele direkt verknüpfte Objekte führen schnell zu unübersichtlichen Abhängigkeiten und «Spaghetti-Code» ( mehr dazu in den Anti-Patterns ). Mediator schafft hier eine zentrale Koordination. 

Beschreibung: Das Mediator-Pattern fördert eine lose Kopplung, indem es die direkte Kommunikation zwischen Objekten verhindert. Stattdessen kommunizieren Objekte über den Mediator.

Anwendungsfälle:

  • Chat-Anwendungen: Verwalten der Kommunikation zwischen mehreren Benutzern in einem Chatroom
  • GUI-Komponenten: Koordinieren von Interaktionen zwischen Schaltflächen, Textfeldern und anderen UI-Elementen
  • Flugsicherung: Steuerung der Kommunikation zwischen Flugzeug und Kontrollturm
  • Workflow Management: Orchestrierung von Schritten in Geschäftsprozessen
  • Event Bus Systems: Zentralisierte Ereignisbehandlung in Microservice-Architekturen
UML
Codebeispiel

State-Pattern

Motivation: Objekte verhalten sich häufig abhängig von ihrem internen Zustand unterschiedlich. Lange if/else- oder switch-Ketten machen den Code unübersichtlich und fehleranfälliger. 

Beschreibung: Mit dem State-Pattern kann ein Objekt sein Verhalten in Abhängigkeit von seinem Zustand dynamisch ändern. Es kapselt zustandsspezifisches Verhalten in verschiedene Klassen.

Anwendungsfälle:

  • Zustandsautomaten: Ampeln, Verkaufsautomaten, Zustände von Spielcharakteren
  • Auftragsabwicklung: Verwalten von Bestellstatus (ausstehend, in Bearbeitung, versendet, geliefert)
  • Media Player: Behandeln von Wiedergabe-, Pause-, Stopp- und Pufferzuständen
  • TCP-Verbindung: Verwalten von Verbindungszuständen (lauschend, aufgebaut, geschlossen)
  • Benutzeroberfläche: Verwalten verschiedener Modi in Anwendungen (Bearbeitungsmodus, Ansichtsmodus, Admin-Modus)
UML
Codebeispiel

Composite-Pattern

Motivation: Es soll dieselbe Funktion gleichermassen auf Einzelobjekte, als auch auf eine Gruppe von Objekten anwendbar sein. Dadurch lassen sich Ganzteilhierarchien darstellen. 

Beschreibung: Das Composite-Pattern wird verwendet, um einzelne Objekte und Kompositionen von Objekten einheitlich zu behandeln. Es eignet sich hervorragend für Systeme, die hierarchische Strukturen wie Dateisysteme erfordern.

Anwendungsfälle:

  • Dateisysteme: Dateien und Verzeichnisse in Datei-Explorern einheitlich behandeln
  • GUI-Komponenten: Verwalten von verschachtelten UI-Komponenten (Panels mit Schaltflächen, Textfeldern)
  • Organigramme: Darstellung von Mitarbeitern und Abteilungen in hierarchischen Strukturen
  • Menüsysteme: Erstellen von verschachtelten Menüs und Untermenüs
  • Zeichnungsanwendungen: Gruppieren von Formen und Behandeln als einzelne Objekte
UML
Codebeispiel

Singleton-Pattern

Motivation: Manche Komponenten oder Klassen sollen in einem System genau einmal existieren und global zur Verfügung stehen. 

Beschreibung: Das Singleton-Pattern beschränkt die Instanziierung einer Klasse auf eine einzelne Instanz und stellt einen globalen Zugriffspunkt darauf bereit. Es wird häufig für die Verwaltung freigegebener Ressourcen wie Konfigurationseinstellungen oder Datenbankverbindungen verwendet.

Anwendungsfälle:

  • Protokollierung
  • Konfigurationseinstellungen
  • Thread-Pools
  • Datenbank-Verbindungspool
UML
Codebeispiel

Chain-of-Responsibility-Pattern

Motivation: Eine Anfrage kann potenziell von mehreren Objekten bearbeitet werden. Der Sender soll aber nicht wissen müssen, welches Objekt dafür zuständig ist. 

Beschreibung: Das Pattern Chain-of-Responsibility ermöglicht es mehreren Objekten, Anforderungen zu verarbeiten, ohne jedoch zu wissen, welches Objekt sie verarbeiten wird.

Anwendungsfälle:

  • Authentifizierungssysteme: Mehrere Authentifizierungsmethoden (Benutzername/Passwort, OAuth, biometrisch)
  • Ausgabengenehmigung: Unterschiedliche Genehmigungsstufen basierend auf den Ausgabenbeträgen
  • Fehlerbehandlung: Sequenzielle Fehlerhandler für verschiedene Arten von Ausnahmen
  • Middleware in Webanwendungen: Pipeline für die Anforderungsverarbeitung (Protokollierung, Authentifizierung, Validierung)
  • Support-Ticket-Systeme: Eskalieren von Tickets durch verschiedene Support-Ebene
UML
Codebeispiel

Klassische Anti-Patterns

Anti-Patterns sind scheinbar nützliche, aber langfristig problematische Lösungsansätze in der Softwareentwicklung. Sie entstehen oft aus Zeitdruck, mangelnder Erfahrung oder dem Versuch, schnelle Ergebnisse zu erzielen. Auf den ersten Blick wirken sie praktisch, führen jedoch zu schwer wartbarem, ineffizientem oder fehleranfälligem Code.

Wir stellen folgende Anti-Patterns kurz vor: 

Schlechte Klasse

Schlechte Klassen sind Klassen mit unscharfen Verantwortlichkeiten, schlechtem Namenskonzept oder zu starker Abhängigkeit von anderen Klassen.

Codebeispiel

Spaghetti-Code

Mit Spaghetti-Code ist unstrukturierter, verschachtelter Code ohne klare Architektur oder Modularisierung gemeint.

Codebeispiel

Copy-Paste-Programmierung

Bei der Copy-Paste-Programmierung wird Code dupliziert statt wiederverwendbar gemacht.

Codebeispiel

Design Anti-Patterns

Missbrauch von Singletons

Wenn das Singleton-Pattern inflationär eingesetzt wird, wodurch enge Kopplung, versteckte Abhängigkeiten und Testprobleme entstehen, spricht man von «Singleton Abuse».

Codebeispiel
Codebeispiel

Architektur Anti-Patterns

Big Ball of Mud (Grosser Schlammball)

Big Ball of Mud bezeichnet eine unstrukturierte Architektur ohne klare Trennung von Verantwortlichkeiten – alles wächst ungeplant zusammen.

Charakteristiken:

  • Keine klare Architektur
  • Eng gekoppelte Komponenten
  • Schwer zu modifizieren oder zu erweitern

Golden Hammer (Goldener Hammer)

Ein vertrautes Werkzeug oder Framework wird für jedes Problem eingesetzt, unabhängig davon, ob es geeignet ist – «Ich kenne JavaScript, also werde ich Node.js für alles verwenden»:

Charakteristische Anzeichen:

  • Web-Frontend: JavaScript ✓
  • Backend-API: Node.js (vielleicht nicht optimal)
  • Datenverarbeitung: Node.js (definitiv nicht optimal)
  • Mobile App: React Native (forciert Web-Tech)

 

Management Anti-Patterns 

Death March (Todesmarsch)

Projekte mit unrealistischen Zielen, die praktisch zum Scheitern verurteilt sind.

Charakteristiken:

  • Unmögliche Fristen
  • Überlastetes Team
  • Qualität wird der Geschwindigkeit geopfert

Analysis Paralysis (Analyselähmung)

Endlose Analyse und Planung verhindern den tatsächlichen Start oder Fortschritt der Entwicklung.

Charakteristiken:

  • Überanalyse von Problemen
  • Niemals Entscheidungen treffen
  • Endlose Meetings ohne Aktion

NIH – «Not Invented Here» 

Ablehnung externer Lösungen oder bewährter Bibliotheken, nur weil sie nicht selbst entwickelt wurden – führt zu unnötiger Doppelarbeit.

 

Prozess Anti-Patterns 

Cargo Cult Programmierung 

Vorgehensweisen oder Technologien werden übernommen, ohne ihr Warum oder ihre Funktionsweise zu verstehen – «Ich habe gehört, dass wir Entwurfs-Pattern verwenden sollten.»

Vorzeitige Optimierung

Die Architektur oder der Code wird optimiert, bevor klar ist, ob die Optimierung überhaupt nötig ist.

 

So vermeidest du Anti-Patterns

  1. Code Reviews – Anti-Patterns frühzeitig erkennen
  2. Refactoring – Regelmässige Verbesserung der Codestruktur
  3. Designprinzipien – Befolge SOLID, DRY, KISS
  4. Testen – Gute Tests decken Designprobleme auf
  5. Lernen – Studiere sowohl Pattern als auch Anti-Pattern
  6. Teamkommunikation – Bespreche Architekturentscheidungen

 

Fazit

Patterns sind mehr als Theorie – sie sind Werkzeug, Sprache und Sicherheitsnetz für Entwicklerteams. Vom Observer bis zum Mediator: Wer die Muster kennt, kann Software stabiler und flexibler gestalten. Genauso wichtig ist es aber, Anti-Patterns frühzeitig zu erkennen und durch bewusste Architekturentscheidungen zu vermeiden.

Vertiefe dein Wissen mit diesen Ressourcen:
Links

•     refactoring.guru – Design Patterns  
•     SourceMaking – Design Patterns erklärt

Buchtipp

Elements of Reusable Object-Oriented Software

Autoren: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides («Gang of Four»)
ISBN: 978-0-201-63361-0

Lesetipp

Noch mehr Klarheit in deinem Code? In unserem News-Artikel erfährst du, wie du mit den SOLID-Prinzipien deine Software wartbar und skalierbar gestaltest.


Schliessen
Stamp Icon-Print Icon-Clear
S
M
L
XL
XXL