Was sind Entwurfsmuster und wozu brauche ich so etwas?

Entwurfsmuster

Entwurfsmuster erhöhen die Lesbarkeit und Qualität von Quellcode

Jeder von Euch, der bereits einmal an einem größeren Software-Projekt, sei es in der Planung oder Implementierung beteiligt war, oder der versucht hat, ein größeres Projekt eines anderen Teams zu lesen, kennt das Problem: Die vielen Teile, die nötig sind, um die gewünschte Funktionalität abzubilden, lenken beim Lesen und Implementieren oft sehr stark vom eigentlichen Zweck des Programms ab.

Besonders “kreative” und außergewöhnliche Lösungen für wiederkehrende Probleme mögen für den Programmierer, der sie produziert, spannend und erfüllend sein, für nachfolgende Programmierer oder Leser des Quellcodes stellen diese Lösungen oftmals Hindernisse bei der Arbeit dar.

Neben diesen Verständnisproblemen, die immer neue Lösungen für wiederkehrende Alltagsprobleme mit sich bringen, gibt es auch noch den Aspekt der Qualität. “Neue” Lösungen für alte Probleme müssen sich erst in der Praxis bewähren, sie müssen ausgiebig getestet werden und auch wenn sich in der Laborumgebung keine Schwierigkeiten ergeben, heißt das noch nicht, dass die Lösung auch im großen Maßstab funktioniert wie geplant.

Ein vorgegebenes “Kochrezept” spart natürlich auch Zeit in der Implementierung, da Ihr Euch keine eigene Lösung ausdenken müsst, sondern ein bewährtes Muster übernehmen und für Eure Zwecke anpassen könnt. Für die Auswahl des richtigen Musters solltet Ihr Euch allerdings, vor allem als Neuling, genügend Zeit nehmen. Ein falsch implementiertes Muster kann zu mehr Problemen führen als gar kein Muster.

In den 90iger Jahren haben vier renommierte Informatiker (Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides) in einem wegweisenden Buch mit dem Titel “Design Patterns – Elements of Reusable Object-Oriented Software” versucht, für die aus ihrer Sicht häufigsten, wiederkehrenden Probleme der objektorientierten Programmierung gute, ausgereifte und getestete Lösungen zu finden, die möglichst einfach wiederverwertet werden können.

Die auch als Gang of four (GoF) bekannt gewordene Gruppe hat damit einen Standard geschaffen und eine Bewegung ins Leben gerufen, die in den letzten 20 Jahren viele Anhänger gefunden hat.

Häufig findet man in den Dokumentationen zum Quelltext größerer Projekte Anmerkungen wie “GoF:57”, dies bezieht sich auf ein bestimmtes Muster, welches im Quelltext Anwendung findet und auf der angegebenen Seite im Lehrbuch gefunden werden kann.

Woran erkenne ich ein Entwurfsmuster?

Neben den Entwurfsmustern, die die GoF in ihrem Buch beschrieben hat, gibt es auch andere Entwurfsmuster. Es gibt aber auch Design-Vorschriften, die explizit keine Entwurfsmuster sind.

Ein Entwurfsmuster beschreibt eine formale Lösung für ein Teilproblem eines Entwurfs. Es geht dabei nicht um die konkrete Implementierung von Logik sondern explizit um den Entwurf objektorientierter Strukturen.

Bei der Definition eines Entwurfsmusters ist neben der konkreten Idee auch immer die Motivation zur Erstellung dieses Musters sowie der Scope (Einsatzbereich) angegeben. Diese Informationen geben vor allem Neulingen wichtige Hinweise, wie das Muster zu verstehen ist und wo man es einsetzen sollte (oder auch, wo es nicht hingehört!).

Algorithmen und Architekturmuster werden häufig mit Entwurfsmustern in Verbindung gebracht, sind aber explizit keine Entwurfsmuster:

Algorithmen sind Rechenvorschriften oder die Lösung eines expliziten programmatischen Problems wie beispielsweise einer Sortieraufgabe. Entwurfsmuster beschreiben, wie Klassen aufgebaut, Objekte erstellt oder eine Gruppe von Klassen arrangiert werden. Auch für den Datenzugriff in objektorientierten Systemen gibt es Entwurfsmuster. Keines dieser Muster beinhaltet jedoch Anweisungen, wie konkrete Probleme gelöst werden.

Obwohl der Namensbestandteil “Muster” im Architekturmuster darauf schließen lassen könnte, dass es sich hierbei um ein Entwurfsmuster handelt, ist ein Architekturmuster von einem Entwurfsmuster klar abzugrenzen. Die Architektur einer (größeren) Applikation besteht aus vielen Teilbereichen, die durch Entwurfsmuster beschrieben werden können. Bevor Entwurfsmuster angewendet werden können, muss eine Architektur ausgewählt werden. Diese ist zunächst abstrakt und wird dann mit Hilfe von Entwürfen für Teilaspekte zum Leben erweckt.

Welche Entwurfsmuster gibt es überhaupt?

Mittlerweile gibt es eine große Bandbreite von Entwurfsmustern. Die klassischen Muster der GoF lassen sich in drei Bereiche einteilen:

Strukturmuster (structural patterns)

Diese Muster sind den Architekturmustern am ähnlichsten. Eine Architektur wird meist in der nächsten Hierarchie-Ebene aus mehreren Strukturmustern gebildet. Diese Muster fassen mehrere Klassen zu einer Klassenstruktur zusammen, die von außen gemeinsam betrachtet und teilweise auch gemeinsam angesprochen werden kann. Adapter, Decorator und Proxy sind beliebte Muster in dieser Kategorie, die häufig zur Anwendung kommen.

Verhaltensmuster (behavioral patterns)

Hier wird beschrieben wie der Datenfluss und das Verhalten zwischen Klassen strukturiert werden kann. Wer sendet Events an wen? Was macht die Klasse im Zusammenhang mit anderen Klassen? Ein berühmtes Verhaltensmuster ist der Iterator, der durch eine Liste iteriert und uns die Möglichkeit gibt, schnell bestimmte Knoten innerhalb von einfach oder mehrfach verketteten Listen zu finden. Beobachter, Memento und Kommando sind weitere, weit verbreitete Muster in dieser Kategorie.

Erzeugungsmuster (creational design patterns)

Diese Muster gehen mit den Strukturmustern meist Hand in Hand. Ist eine Struktur einmal definiert, macht es Sinn, sich um ein einheitliches Verhalten bei der Erzeugung von Objekten aus Klassendefinitionen zu bemühen. Das Singleton-Muster dürfte in dieser Kategorie das bekannteste sein. Sein Zweck besteht darin sicherzustellen, dass von einer bestehenden Klassendefinition nur exakt eine Objekt-Instanz erzeugt wird. Diese Vorgabe ist in manchen Fällen für die Stabilität und Sicherheit einer Applikation von größter Bedeutung. Beispielsweise darf es in einem Programm, das mit genau einer Hardware an einer seriellen Schnittstelle kommuniziert, nur eine Kommunikationsinstanz für die Schnittstelle geben.

Kann man diese Muster auch bei einem testgetriebenen Ansatz wählen?

Ja! Ihr habt gelernt, dass beim TDD (test driven design) zunächst ein Test geschrieben wird, der ohne Code natürlich nicht bestanden werden kann. Dann werden genau genug Zeilen Code geschrieben, um den Test zu bestehen. Anschließend wird der nächste Test geschrieben und man beginnt den Prozess von vorne. Am Ende hat man dann einen großen Haufen Spaghetti-Code und eine Sammlung von Tests, die bei jedem Compiliervorgang automatisch ausgeführt werden.

Dies ist natürlich nicht das Ende von TDD. Ein wichtiger Bestandteil dieser Technik ist das Refactoring. Hier wird aus dem unstrukturierten Code eine wohlgeformte Applikation mit Dokumentation und erkennbaren, wartbaren Strukturen geschaffen. Dadurch, dass die Tests, welche das generelle Funktionieren der Applikation immer wieder überprüfen, bei jedem Compiliervorgang ausgeführt werden, seid Ihr bei Euren Aufräumarbeiten immer auf der sicheren Seite: Solltet Ihr etwas kaputt machen, dann merkt Ihr es sofort.

Es macht in der Praxis Sinn, TDD immer im Zusammenhang mit klassischer Modellierung zu betrachten. Wenn Ihr einmal eine Architektur festgelegt habt und einzelne use cases definiert habt, könnt Ihr mit der Erstellung von Tests loslegen und die konkrete Logik implementieren. Ist ein Teilbereich fertiggestellt, macht es Sinn, sich zurückzulehnen, über die Struktur und konkreten Schnittstellen zu anderen Teilbereichen der Applikation nachzudenken und den vorhandenen Code dann zu refactoren. Im Anschluss kann der nächste Teilbereich implementiert werden.

Muster um jeden Preis?

Ein Fehler, den vor allem Neulinge in der Welt der Entwurfsmuster machen, ist zu versuchen, jedes erdenkliche Problem in ein bestehendes Muster zu pressen. Manchmal macht es Sinn, ein Problem einfach selbst zu lösen. Manchmal ist es schwer, das richtige Muster zu finden. In jedem Fall sollten Muster, wie auch alle anderen Hilfen bei der Lösung von Problemen in der Informatik, mit Augenmaß betrachtet werden.

Rückmeldungen