Von Doppelgängern und Handwerkern

Hallo Spaß-Coder.

In unserem letzten Artikel zur Serie über Entwurfsmuster haben wir uns mit dem Einzelstück (singleton) beschäftigt. Es gibt aber noch weitere sog. Erzeugungsmuster. Mit diesen werden wir uns im Rahmen dieses Artikels beschäftigen.

Die verbleibenden Erzeugungsmuster aus der Sammlung der Gang of Four sind:

  • Prototyp (prototype)
  • Erbauer (builder)

Auch diese beiden Muster dienen dazu, ein Objekt zu erstellen, mit dem wir dann weiterarbeiten können. Aber warum wollen wir das? Hier gelten die gleichen Gründe, die wir auch schon bei der Abstrakten Fabrik angeführt haben. Wir wollen möglichst auf new verzichten, wenn wir uns in Teilen des Codes bewegen, die sich mit hoher Wahrscheinlichkeit ändern werden.

Der Prototype verhält sich genau anders herum als ein Singleton. Immer dann, wenn wir uns ein Objekt des Prototypen holen, ist dies eine neue Instanz. Allerdings ist diese Instanz nicht immer ganz neu, sondern wird als Kopie einer Vorlage erstellt. Anschließend kann der Prototyp dann an die aktuellen Bedürfnisse angepasst werden.

Der Builder erzeugt ein komplexes Objekt, indem er es aus einzelnen Bestandteilen zusammensetzt. Dieses Muster dient entweder der Wiederverwendung, wenn das Objekt aus verschiedenen Quellsystemen heraus erstellt werden soll oder es dient schlicht der Vereinfachung der Objekterstellung. Beim ersten Anwendungsfall möchten wir wieder die Aspekte der Erstellung und der Repräsentation voneinander trennen (Separation of Conerns), beim zweiten geht es eher um besser lesbaren Code.

 

Wann sind diese Entwurfsmuster sinnvoll anzuwenden?

Prototype

Der Prototype hilft uns insbesondere dann, wenn die Erzeugung von Instanzen einer Klasse aufwendig oder kompliziert ist und die erstellten Objekte einander sehr ähnlich sind.

Am besten vergleichen lässt sich das Muster mit der Erstellung von Dokumenten. Damit ich möglichst schnell mit dem Erfassen des Inhalts starten kann, erstelle ich mein neues Dokument in der Regel nicht leer, sondern aus einer Vorlage, einem sog. Template.

Der Kern des Musters ist, dass ich aus einer Vorlage eine Kopie erstellen kann, die ich im Folgenden dann weiterverwende. Die Erstellung der Kopie hat das Ziel, eben nicht teuer, sondern möglichst schlank zu sein.

 

Beispiel

Nehmen wir einmal, wir möchten Gespräche dokumentieren. Dazu haben wir ein Formular auf einer Webseite, in das wir für die verschiedenen Gesprächspunkte Einträge machen können. Zusätzlich gibt es in dem Gesprächsprotokoll spezielle Eingaben, wie etwa das Datum, die Liste der Beteiligten und zum Schluss die Möglichkeit, Feedback in Form einer Bewertung abzugeben.

Von dieser Art der Gesprächsprotokolle gibt es die verschiedensten Variationen:

  • Mitarbeitergespräch zwischen Mitarbeiter und der Führungskraft
  • Interview für eine Marketingkampagne
  • Fragebogen zur Mitarbeiterzufriedenheit
  • Interview mit dem Stakeholder für die agile Umsetzung einer User-Story

Jedes dieser Formulare lässt sich nur sehr aufwendig erstellen. Hier müssen die Definitionen aus der Datenbank gelesen werden, bestimmte Steuerelemente initialisiert werden, Reihenfolgen festgelegt werden, …

Damit dies nicht jedes Mal aufs neue manuell durch den Client gemacht werden muss, ist es über das Prototype-Muster möglich, die Erzeugung in einer Klasse zu kapseln (etwa in eine Factory) und nur einmalig durchzuführen. Sobald das Formular ein zweites Mal für ein anderes Gespräch verwendet werden soll, wird das Template einfach dupliziert und an den Client zurückgegeben.

Im Beispiel nutzen wir für die Bestandteile des Gesprächs die Praxis des Copy-Constructors. Dabei wird dem Konstruktor eine Instanz vom gleichen Typ wie der Klasse in der wir uns befinden übergeben. Die einzelnen Werte werden dann einfach den Klassenvariablen zugewiesen.

 

Hinweis

Auch der Prototype lässt sich durch Frameworks – wie etwa Spring – unterstützt umsetzen. Dies deckt zwar nicht alle Anwendungsfälle ab, sollte aber immer, wenn möglich genutzt werden.

 

Vermischung von Mustern

Denkbar ist es, dass wir Muster miteinander kombinieren. Wenn es z.B. eine Familie von Produkten gibt, die alle recht ähnlich sind, aber in der Erstellung recht teuer, kann man darüber nachdenken, eine Abstrakte Fabrik zu erstellen, dessen Produkte Prototypen sind. Auch die Verwendung im Zusammenspiel mit einem teuer zu erstellenden Singleton ist denkbar.

 

Builder

Das Muster des Builders verwenden wir immer dann, wenn die Erstellung eines komplexen Objekts unabhängig von der Erzeugung der Bestandteile sein soll. Wenn es also keine Rolle spielt, in welcher Reihenfolge die Bestandteile des Objekts erzeugt werden.

Eine zweite Möglichkeit, den Builder einzusetzen ist, wenn die Erstellung eines Objekts einen internen Zustand erfordert, der dem Ersteller aber nicht bekannt ist oder bekannt sein soll.

Beispiel

Es gibt verschiedene Möglichkeiten, einen Builder zu implementieren und die Instanz hinterher abzurufen. Typisch ist etwa eine statische, interne Klasse Builder innerhalb des Objekts, welches erstellt werden soll. Dies sieht dann wie folgt aus:

Hier wird der Builder also dem Konstruktor des zu erstellenden Objekts übergeben. Dies hat den Vorteil, dass das Objekt auch mit anderen Buildern erstellt werden kann, etwa zu Testzwecken.

Eine andere Möglichkeit kann auch sein, dass der Builder selbst das Objekt erstellt und dann zurück gibt. Welche Art gewählt wird hängt im Grunde vom Objekt ab und sollte je nach Situation gewählt werden.

Als sinnvoll hat sich herausgestellt, dass der Builder in Form eines Fluent Interface implementiert wird. Dies vereinfacht den Aufruf und macht den Code zur Erstellung deutlich besser lesbar.

 

Zusammenfassung

Die in diesem Artikel vorgestellten Muster runden die Liste der Erzeugungsmuster aus der Sammlung der Gang of Four ab. Sie dienen zur  Erstellung von Objekten in besonderen, immer wiederkehrenden Situationen.

Wir hoffen, dass die Beispiele euch genügend Anhaltspunkte dafür gegeben haben, wann eines der Muster sinnvoll eingesetzt werden kann.

 

Viel Spaß beim Ausprobieren

Eure Spaß-Coder

 

Den Code zu unseren Beispielen könnt ihr auf Github finden:

  • https://github.com/invidit/CodeQuality/tree/master/DesignPattern/Prototype
  • https://github.com/invidit/CodeQuality/tree/master/DesignPattern/Builder

 

Dieser Artikel basiert neben unseren Erfahrungen auf den Ausführungen aus:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.