Suchen

Software Design Patterns

| Autor / Redakteur: Karl Nieratschker / Sebastian Gerstl

Viele Entwickler besitzen einen Vorrat an allgemein anwendbaren Entwurfsmustern, mit deren Hilfe flexible, leicht anpassbare und gut wartbare Applikationen entwickelt werden können. Aber der Versuch, diese Software Design Patterns in der Praxis anzuwenden, stellt sich oft schwieriger heraus als erwartet. Dies kann viele Ursachen haben.

Firma zum Thema

Software Design Patterns bzw. Entwurfsmuster versprechen schnell anwendbare, vorgefertigte Lösungen in der objektorientierten Programmierung. Doch die praktische Anwendung von Patterns erweist sich oft schwieriger als gedacht.
Software Design Patterns bzw. Entwurfsmuster versprechen schnell anwendbare, vorgefertigte Lösungen in der objektorientierten Programmierung. Doch die praktische Anwendung von Patterns erweist sich oft schwieriger als gedacht.
(Bild: Clipdealer)

Design Patterns sind standardisierte Lösungsansätze für häufig wiederkehrende Aufgabenstellungen des objektorientierten Softwareentwurfs. Bekannt geworden sind sie erstmals durch die Veröffentlichung des Standardwerks „Design Patterns: Elements of Reusable Object-Oriented Software“ von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides, der sogenannten „Gang of Four“.

Prinzipiell sollen diese Entwurfsmuster eine wiederverwendbare Vorlage zur Problemlösung darstellen, die in einem bestimmten Zusammenhang einsetzbar ist. Beim Versuch, die Patterns in der Praxis anzuwenden, stellen Softwareentwickler allerdings häufig fest, dass dies schwieriger ist, als erwartet. Dies kann viele Ursachen haben, die man erkennen und beseitigen muss, wenn die Entwurfsmuster ihren Zweck erfüllen sollen.

Bildergalerie

Erwerb von Entwurfsmustern: Wie kommt man zum Pattern-Know-how?

Ein naheliegender Weg ist natürlich die Einarbeitung mithilfe des Internets, wo man eine Vielzahl von Beispielen zu diesem Thema findet. Diese Vorgehensweise ist aber nur bedingt empfehlenswert, denn dort findet man leider nicht nur gute Beispiele. Wichtig für ein gutes Verständnis ist, dass der Autor des Beispiels das Thema gut (und richtig!) verstanden hat und den Sachverhalt so darstellen kann, dass man ihn ohne weitere Vorkenntnisse gut nachvollziehen kann.

Leider erfüllen nicht alle Beispiele diese Kriterien. Erschwerend kommt hinzu, dass man die Richtigkeit und Qualität eines Beispiels als Anfänger natürlich noch nicht beurteilen kann. Dies ist besonders dann problematisch, wenn man sich aufgrund von Zeitmangel auf ein einziges Beispiel beschränken muss, was in der Praxis aus Zeitgründen oft der Fall ist. Dies kann nämlich dazu führen, dass eventuell vorhandene Anwendungsfehler mangels Vergleichsmöglichkeiten unbemerkt ins Design übernommen werden.

Mitunter besteht das Problem auch drin, dass die Beispiele, der leichteren Verständlichkeit halber, so stark vereinfacht sind, dass man zwar versteht, wie das Pattern programmiert werden soll, sich aber fragt, warum man für so ein vermeintlich triviales Problem überhaupt ein Pattern verwenden soll. Dadurch entsteht leicht der Eindruck, dass Entwurfsmuster unnötig kompliziert sind und für Dinge eingesetzt werden, die man auf herkömmliche Art sehr viel einfacher lösen könnte. In der Tat ist dies ein weit verbreitetes Vorurteil.

Genau das gegenteilige Problem hat man oft mit „Praxisbeispielen“, die aufgrund ihrer Realitätsnähe eigentlich besonders nützlich sein sollten. Gerade diese Praxisnähe führt in der Regel aber zu mehr Komplexität, durch die es schwerer wird, die für das Verständnis wichtigen patternrelevanten Dinge vom restlichen Applikationskontext zu trennen.

Anwendung von Entwurfsmustern in der Praxis

Paradoxerweise ist gerade die Einarbeitung mithilfe vieler konkreter Beispiele oft der Grund, der die Entwurfsmusteranwendung im Allgemeinen schwierig macht. Denn je mehr konkrete Lösungen man kennt, umso mehr neigt man zu einem lösungsorientierten Ansatz, bei dem versucht wird, aus der Menge der bekannten Beispiellösungen eine zu finden, deren Programmiermodell auch im aktuellen Fall passen könnte. Dies ist aber eine sehr aufwändige Vorgehensweise, da die Applikationskontexte verschieden sind und die gegebenen Lösungen deshalb gedanklich auf den neuen Anwendungskontext übertragen werden müssen.

In dieser Situation ist es besser, mit einem problemorientierten Ansatz an die Sache heranzugehen. Die Definition jedes Patterns stellt Informationen sowohl über Sinn und Zweck, als auch zur Anwendbarkeit des Patterns in Form einer möglichst allgemeingültigen textuellen Beschreibung zur Verfügung. Um feststellen zu können, ob ein aktuelles Problem mithilfe eines Patterns gelöst werden kann, muss also nur geprüft werden, ob es ein Muster gibt, dessen Sinn- und Zweck zum gegebenen Problem passt. Auf dieser Ebene geht es ausschließlich darum, ob ein Pattern das Problem prinzipiell lösen könnte, nicht wie das Pattern das Problem lösen würde! Da die Implementierung hier noch keine Rolle spielt, lässt sich so sehr viel einfacher feststellen, ob ein Pattern geeignet ist.

Implementierung des Entwurfsmusters

Die Patterndefinition beschreibt, welche Klassen auf welche Weise zusammenarbeiten müssen, um das gewünschte Verhalten zu erzielen. Neben der textuellen Beschreibung gehört dazu auch immer eine Art „Bauplan“ in Form einer grafischen Darstellung.

Um eine Patternimplementierung möglichst applikationsunabhängig einsetzen zu können, sind die Elemente der Diagramme grundsätzlich in abstrahierter Form dargestellt. Die Klassen des Patterndiagramms und deren Methoden repräsentieren im Prinzip die Rollen bzw. Operationen, die im zu entwickelnden Entwurf durch bereits existierende oder neu zu erstellende konkrete Klassen bzw. Methoden realisiert werden müssen. Trotz des vorhandenen Bauplans fällt vielen Entwicklern die Konkretisierung des Patternmodells oft nicht so leicht wie erwartet. Häufig liegt es an der Abstraktion des Patternmodells, weil Rollenbezeichnungen wie „Subject“ oder „Component“ so nichtssagend sind, dass es einfach nicht intuitiv ist, was eine konkrete Klasse hier tun soll. Spätestens hier wird deutlich, dass man für das Verständnis eines Patterns nicht nur das Diagramm, sondern auch die textuelle Beschreibung kennen muss. An dieser Stelle können nun auch die eingangs erwähnten Anwendungsbeispiele nützlich sein.

Entwurfsmuster sollte man sich also vorrangig anhand ihres Zwecks merken. Hat man nur die Implementierung im Kopf, dann fällt es schwer, Stellen zu finden, wo ein Muster eingebaut werden kann. Dazu kommt, dass nicht jedes Pattern eine eigene Implementierung besitzt! Entwicklern, die sich an der Implementierung orientieren, kann es deshalb schwerfallen, Muster auseinanderzuhalten bzw. zu erkennen, welches Pattern an welcher Stelle verwendet werden kann.

Trennung von Zweck und Implementierung

Wie wichtig die klare Trennung zwischen dem Zweck eines Patterns und seiner Implementierung ist, lässt sich z.B. am Beobachtermuster (Observer-Pattern) zeigen. Dieses Pattern kann angewendet werden, wenn ein Objekt über die Zustandsänderungen eines anderen Objektes informiert sein muss (Sinn und Zweck des Patterns). In der Patterndarstellung wird die Klasse des an der Zustandsänderung interessierten Objektes als ConcreteObserver, und die Klasse des Objektes, dessen Zustand sich ändert als ConcreteSubject bezeichnet.

Im Patternmodell „beobachtet“ also ein ConcreteObserver ein ConcreteSubject, um über dessen Zustand informiert zu sein. Somit spielt der Beobachter eine aktive und das Subjekt eine passive Rolle. Dies ist allerdings völlig konträr zum Implementierungsmodell des Patterns! Tatsächlich wird das Muster nämlich so implementiert, dass das (aktive) Subjekt den (passiven) Beobachter jedes Mal informiert, wenn sich sein Zustand ändert. Diese Widersprüchlichkeit der Modelle führt oft zur Verwirrung und erschwert die Anwendung des Patterns

Praktische Erfahrungen beim Einsatz von Software Design Patterns

Selbstverständlich ist das theoretische Wissen der Patternanwendung allein noch nicht ausreichend. Die Anwendung von Patterns muss natürlich auch intensiv praktiziert werden, einerseits um einen Blick für die Möglichkeiten zu bekommen, wo Patterns eingesetzt werden können, und andererseits, um ein Gefühl dafür zu entwickeln, was es heißt, ein abstraktes Patternmodell auf eine konkrete Anwendung abzubilden. Für den Lernerfolg ist es auf jeden Fall wichtig, zumindest in der Anfangsphase des Know-how-Aufbaus einen erfahrenen Ansprechpartner zu haben, mit dessen Hilfe die Richtigkeit und Qualität der Entwurfsentscheidungen überprüft werden können. Ohne diese Kontrolle ist man in einer ähnlichen Situation, wie jemand, der eine neue Programmiersprache lernen soll, ohne einen Compiler dafür zu haben. Nur mithilfe der Rückmeldungen eines solchen „Design-Compilers“ kann man wissen, ob man das richtige Pattern an der richtigen Stelle seinem Zweck entsprechend eingesetzt hat.

Da in der Praxis nur die wenigsten Entwickler permanent neue Software entwerfen können, um den Umgang mit Design Patterns zu üben, sollten auch andere Gelegenheiten zum Sammeln von praktischen Erfahrungen genutzt werden. Dazu gehört z.B. die Analyse der existierenden Software, um feststellen (und dokumentieren) zu können, an welchen Stellen Entwurfsmuster eingesetzt werden könnten. Wenn die Software zu einem späteren Zeitpunkt erweitert oder ein Fehler behoben werden muss, können die identifizierten Muster bei dieser Gelegenheit gleich angewendet werden. Eine andere Möglichkeit, Erfahrungen zu sammeln, besteht darin, herauszufinden, an welchen Stellen Design Patterns in Open Source Software verwendet wurden. Dies trägt einerseits zur Verinnerlichung der Entwurfsmusterdefinitionen bei und zeigt andererseits, um wieviel leichter es ist, unbekannte Software zu verstehen, wenn bekannte Muster verwendet wurden.

Wie komme ich mit einem Entwurfsmuster zum befriedigenden Ergebnis

Entwurfsmuster sind heute unverzichtbar für einen guten objektorientierten Softwareentwurf. Trotz der Verfügbarkeit vieler Beispiele führt der Versuch, Design Patterns selbst anzuwenden, oft zu unbefriedigenden Ergebnissen. Voraussetzung für den Erfolg ist vor allem ein gutes Verständnis des verallgemeinerten Lösungsansatzes, der hinter jedem Pattern steht. Auf dieser Basis lässt sich leichter als mit Beispielen feststellen, welches Muster an welcher Stelle verwendet werden kann. Generell muss zwischen Zweck und Implementierung des Musters genau unterschieden werden. Bei der Realisierung des Patterns müssen die in der verallgemeinerten Patternlösung beschriebenen Rollen von konkreten Applikationsklassen übernommen werden.

Die Schwierigkeit dieses Abbildungsprozesses wird in der Praxis oft unterschätzt. Da es keine Tools zur Überprüfung der Qualität einer Entwurfsmusteranwendung gibt, ist es wichtig, einen erfahrenen Ansprechpartner zu haben, mit dessen Hilfe festgestellt werden kann, ob das Design noch korrigiert oder verbessert werden muss.

* Karl Nieratschker ist als selbständiger Trainer und Coach seit vielen Jahren im Bereich der Softwareentwicklung für Embedded Systeme tätig. Seine Spezialgebiete sind objektorientierte Programmierung in ressource-limitierten Systemen, sowie Multithread-/Multicore-Programmierung.

(ID:45547473)

Über den Autor

 Karl Nieratschker

Karl Nieratschker

Software Trainer und Berater für Embedded Systeme