Grundlagen der Programmierung: Entwurfsmuster

Abläufe anpassen, aber nicht ändern: Schablonenmethode (Template Method)

Testen Sie unsere 2012 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Möchten Sie die Grundstruktur eines Ablaufs vorgeben, einige der Einzelschritte aber anpassbar gestalten, dann sollten Sie sich die "Schablonenmethode" anschauen.
07:40

Transkript

In diesem Video möchte ich Ihnen das Entwurfsmuster Schablonenmethode vorstellen. Dabei geht es um folgende Problemstellung. Für eine bestimmte Art von Aufgaben in Ihrer Anwendung ist der grundsätzliche Ablauf bereits fest vorgegeben. Es steht also fest, welche Schritte ausgeführt werden müssen und in welcher Reihenfolge. Je nach konkreter Anwendung können sich aber Details dieses Algorithmus unterscheiden. Das heißt, die Reihenfolge der Schritte bleibt zwar weiterhin gleich, nur an einigen ausgewählten Stellen passieren unterschiedliche Dinge. Die Grundsätzliche Struktur des Ablaufs bleibt dabei aber unverändert. Ein typisches Beispiel dafür sind Sortieralgorithmen. Wenn ich Objekte sortieren möchte, dann ist das grundsätzliche Vorgehen immer das Gleiche. Aber je nachdem ob ich zum Beispiel aufsteigend oder absteigend sortieren möchte oder ob ich meine Kunden nach Name oder nach Kundennummer sortieren möchte, sind die Algorithmen im Detail dann doch unterschiedlich. Ein weiteres Beispiel ist das Versenden von Daten über das Netzwerk. Der grundsätzliche Ablauf ist dabei immer Verbindung aufbauen. Daten senden. Verbindung abbauen. Wenn ich nun zum Beispiel eine http-Verbindung habe und eine smtp-Verbindung, dann ist das eigentliche Versenden der Daten als Bytestrom vermutlich recht ähnlich. Das Aufbauen und Abbauen der Verbindungen ist aber unterschiedlich und die Reihenfolge dieser drei Schritte muss natürlich auch eingehalten werden und auch beim Darstellen von Komponenten in einer grafischen Oberfläche können wir diese Anforderungen wiederfinden. Schauen wir uns dieses letzte Beispiel einmal genauer an. Eine typische grafische Oberfläche lässt sich als hierarchische Anordnung von Komponenten betrachten. Wir haben hier ein Hauptfenster, das zwei Unterfenster, zwei Teilbereiche enthält. Im linken Bereich oben, das soll eine Tabelle darstellen und darunter haben wir zwei Eingabezeilen jeweils mit einem Label dran. Die Tabelle wiederum hat natürlich wieder Unterelemente, nämlich ihre Zeilen und Spalten. Auf der rechten Seite sehen wir dann noch eine Eingabezeile mit einem Text dran und darunter einen Button, der sich drücken lässt. Wenn ich nun dieses ganze Fenster darstellen möchte, dann muss ich der Reihe nach drei Schritte ausführen. Zuerst muss der Rahmen gezeichnet werden. Dann wird der Inhalt, der Hintergrund gezeichnet und schließlich die Unterelemente des Fensters. Bei den Unterelementen sieht es dann wieder genauso aus. Zuerst Rahmen zeichnen, dann den Inhalt, den Hintergrund usw. und schließlich die Unterelemente. Wenn der Vorgang für alle Unterelemente abgeschlossen ist, dann ist das Fenster fertig gezeichnet. Wenn wir uns das näher anschauen, dann stellen wir folgendes fest. Das Zeichnen des Rahmens, also der erste Schritt, läuft eigentlich immer auf die gleiche Weise ab. Die jeweilige Komponente schaut einfach nach, wie breit und wie hoch sie ist und zeichnet einen Rahmen. Auch der dritte Schritt ist eigentlich immer der gleiche. Zuerst nachschauen, ob man überhaupt Unterelemente hat. Die Tabelle hat zum Beispiel welche, der Button nicht, und wenn Unterelemente da sind, einfach der Reihe nach zu jedem hingehen und ihm mitteilen, dass er sich auch darstellen soll. Beim Hauptfenster wird zum Beispiel ein Hintergrundbild gezeichnet. Beim Button hingegen wird dieser Ok-Text dargestellt. Das heißt, wir haben hier genau die beschriebenen Anforderungen. Ein grundsätzlicher Algorithmus ist fest vorgegeben. In unserem Fall besteht er aus drei Schritten und Teile des Algorithmus sind ebenfalls bereits vorgegeben, in unserem Fall also Schritt 1 und Schritt 3. Teile des Algorithmus variieren aber, in unserem Fall also der zweite Schritt. Nun wissen wir also, wie das Problem aussieht und damit können wir uns der Lösung zuwenden. Hier noch mal die Grundstruktur unseres Algorithmus. Schritt 1 Rahmen zeichnen. Schritt 2 Inhalt zeichnen. Schritt 3 Unterelemente zeichnen. Es geht hier um das Zeichnen von Komponenten. Also beginnen wir mit einer Klasse Komponente. Wir können den grundsätzlichen Ablauf unseres Algorithmus in eine Methode hineinpacken, die wir dann zum Beispiel zeichne nennen und auch die Implementierung der Methode geben wir schon vor, nämlich zeichneRahmen, zeichneInhalt, zeichneKinder. Dieser grundsätzliche Ablauf sollte auch nicht mehr geändert werden können, deshalb habe ich diese Methode als final gekennzeichnet. Das heißt, eventuelle Unterklassen können diese Methode auch nicht mehr überschreiben. Der grundsätzliche Algorithmus steht. Jetzt brauchen wir natürlich noch die einzelnen Methoden dazu. Schritt 1, also zeichneRahmen, können wir bereits in dieser Klasse implementieren. Einfach Breite und Höhe rausfinden und entsprechend ein Rechteck zeichnen. Schritt 3, zeichneKinder, ist zwar minimal komplexer, aber auch diese Methode können wir bereits in dieser Klasse implementieren. Wir müssen einfach nur nachschauen, gibt es Kinder und wenn ja, dann gehen wir zu jedem einzelnen hin und sagen jedem Kinde ebenfalls wieder, zeichne dich, womit wir dann also für jedes einzelne Kind ebenfalls wieder diesen Algorithmus lostreten. Wie Sie wahrscheinlich schon sehen können, ist diese Klasse so angelegt, dass sie wunderbar als Basisklasse geeignet ist. Das einzige, was diese Basisklasse noch nicht wissen kann, ist, wie zeichneInhalt umgesetzt werden soll. Es wurde zwar schon festgelegt, an welcher Stelle im Verlauf diese Methode aufgerufen werden soll, aber eben noch nicht, was passieren soll, wenn sie aufgerufen wird. Da es also noch keinen sinnvollen Inhalt gibt, deklarieren wir die Methode als abstrakt. Nun können wir unsere erste Unterklasse bauen. Nennen wir sie Fenster. Diese Klasse erbt nun unsere Hauptmethode zeichne, sowie die beiden Teilimplementierungen zeichneRahmen und zeichneKinder. Da die Methode zeichneInhalt abstrakt ist, muss diese nun von der Unterklasse Fenster implementiert werden. Die Unterklasse Fenster leistet also ihren ganz persönlichen Beitrag zum Algorithmus, muss den aber nicht nochmal komplett neu implementieren, sondern nur das, was sich eben in der Unterklasse Fenster ändert. Wenn ich nun ein solches Fenster habe und rufe zeichne auf, dann wird also zuerst der Rahmen gezeichnet. Dann wird der Inhalt des Fensters gezeichnet, so wie in der Klasse Fenster spezifiziert. Schließlich werden noch die Kinder des Fensters gezeichnet. Nun können wir uns für jede Art von Komponente in unserer grafischen Oberfläche eine entsprechend Unterklasse von Komponente bauen. Für die Tabelle zum Beispiel eine Klasse Tabelle, in der wir natürlich zeichneInhalt überschreiben müssen. Oder für unsere Eingabezeile eine Klasse Eingabezeile und auch hier müssen wir natürlich zeichneInhalt überschreiben. So funktioniert also das Entwurfsmuster Schablonenmethode. Wir legen in der Basisklasse eine Schablonenmethode an, also eine Vorlage, die festlegt, wie der Algorithmus grundsätzlich auszusehen hat. Die Stellen des Algorithmus, die von den Unterklassen ausgefüllt werden müssen, markieren wir als abstrakt. Und schließlich sorgen wir noch dafür, dass der Algorithmus als solcher nicht mehr geändert werden kann, indem wir die Methode als final deklarieren. Die Unterklassen erben nun diesen Algorithmus, implementieren die fehlenden Methoden und sind fertig. Fassen wir die Vorteile noch einmal zusammen. Dadurch dass wir die Schablonenmethode in die Basisklasse legen, haben wir also eine zentrale Stelle, an der der Algorithmus definiert wird. Einzelne Schritte des Algorithmus sind in Unterklassen anpassbar. Die Grundstruktur bleibt aber unverändert. Dann gibt es noch eine Sache, die habe ich bisher nicht erwähnt, ist es nämlich auch möglich, den Unterklassen optionale Erweiterungsmöglichkeiten zu bieten. In unserem Bespiel hatten wir eine Methode, die von allen Unterklassen überschrieben werden musste. Nun ist es aber natürlich auch möglich, dass wir in unserer Basisklasse ganz normalen Methoden zur Verfügung stellen, die auch als Teil des Algorithmus aufgerufen werden, die aber zuerst einmal gar nichts tun, also eine Leerimplementierung haben. Auf diese Weise kann die Unterklasse diese Methode überschreiben, muss es aber nicht. Wird die Methode nicht überschrieben, passiert einfach gar nichts an der Stelle und wird sie überschrieben, dann wird auf diese Weise an einer genau festgelegten Stelle im Algorithmus entsprechend diese zusätzliche Funktionalität ausgeführt. Eine der Hauptvorteile und eine Intension dieses Musters ist gleichzeitig eine der Nachteile. Nämlich der Algorithmus kann nicht verändert werden. Das heißt, ich kann zwar Detailanpassungen vornehmen, den grundsätzlichen Ablauf kann ich aber nicht verändern. Das ist es in diesem Fall sogar gewünscht. Sollte es aber ein Problem sein, dann kommt das Muster vielleicht eher nicht in Frage. Weiterhin ist der Kontrollfluss schwerer nachvollziehbar. Insbesondere wenn man sich nur die Unterklasse anschaut und den dahinterliegenden Algorithmus nicht kennt, dann ist nur schwer zu erkennen, was da eigentlich genau in welcher Reihenfolge passiert. Und schließlich wird das ganze Konzept mit zunehmender Anzahl zu überschreibender Methoden immer schlechter handhabbar. Wenn ich in der Unterklasse 20 oder 30 Methoden implementieren muss, dann ist das schon ein ziemlicher Aufwand abzusichern, dass es auch alles funktioniert, wie es soll und anders herum, wenn wir zu wenig abstrakte Methoden zum Überschreiben anbieten, dann kann es zu zu geringer Flexibilität in den Unterklassen führen. Sie müssen also sehr sorgfältig abwägen, um die richtige Mitte zu finden. Damit kennen Sie nun die Schablonenmethode.

Grundlagen der Programmierung: Entwurfsmuster

Erhalten Sie einen fundierten Einstieg in das Thema Entwurfsmuster (Design Patterns) und erfahren Sie, wie Sie Entwurfsmuster in objektorientierten Programmiersprechen umsetzen.

2 Std. 49 min (33 Videos)
Hervorragendes Videotraining
Mathias B.

Das Videotraining gibt meiner Meinung nach einen prima Überblick über die gebräuchlichsten Entwurfsmuster. Mir gefiel auch, wie der Autor diese Erläutert hat: Vom Abstrakten (anhand von Powerpoint-Präsentation), zum Code-Beispiel (da wo es nötig ist) bis hin zur Diskussion mit Vor- und Nachteilen. Ich wünschte mir, dass auch andere Video-Trainings mit ähnlich komplexem Inhalt so präsentiert würden. Weiter so!

 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!