Factorys sinnvoll für kurzen Code nutzen

Heute möchte ich einmal eine andere Möglichkeit aufzeigen, wie man Factorys sinnvoll nutzen kann: Um schnell und mit geringem Code-Aufwand komplexe Objekte zu erzeugen und zu konfigurieren. Ich hoffe, dass ich keinem Anhänger der „Gang of Four“ zu sehr auf die Füße trete, hat dieser Ansatz doch vergleichsweise wenig mit der „Factory Method“ oder „Abstract Factory“ zu tun und ist eher ein Ansatz, um mit bestehenden Konventionen zu brechen.

Das Problem am Beispiel „GridData“

Das erste mal bin ich auf den Ansatz aufmerksam geworden, als ich in Eclipses SWT Layouts konfigurieren wollte. Hierfür braucht man, abhängig vom gewählten Layout, Layout-Data-Objekte wie zum Beispiel GridData. Dieses hat ziemlich viele Einstellungen und wird vergleichsweise oft gebraucht. Die folge ist eine Menge Schreibarbeit und unschöner Code.

GridData listBoxData = new GridData(GridData.FILL_BOTH);
listBoxData.widthHint = 150;
listBoxData.heightHint = 150;
listBoxData.minimumWidth = 1;
listBoxData.minimumHeight = 1;
listBox.setLayoutData(listBoxData);

Dies ist lediglich ein Objekt. Bei geschachtelten Layouts benötigt man aber schnell einige davon.

Oft versucht man, die mögliche Konfiguration über Konstruktoren zu erreichen. Dies ist bei einer Vielzahl an Werten, die zudem noch optional sind, oft alles andere als praktikabel. Außerdem kann man versuchen, die verschiedenen Data-Objekte in einem Baum anzuordnen und Kinder mittels Klonen zu erzeugen, um den Code zu verkleinern. Das macht eine so kleine Sache aber wieder zu einer Wissenschaft, die einfach nicht nötig ist.

Die Factory-Lösung „GridDataFactory“

Eine geeignete Lösung: Eine Factory für die widerspenstige Klasse. Für das GridData-Objekt sieht das ganze dann so aus:

GridDataFactory.fillDefaults()
	.grab(true, true).hint(150, 150).applyTo(listBox);

Eine elegante Lösung, die man verallgemeinern und an vielen Stellen anwenden kann.

Struktur einer Factory

Für eine beliebige Klasse Foo, von der wir Objekte erstellen wollen, empfiehlt sich, für die Factory eine eigene Klasse zu nehmen. Prinzipiell lassen sich die folgenden Methoden auch in Foo direkt integrieren, das bläht die Klasse aber nur unnötig auf. Schließlich sind alle Methoden öffentlich und somit der Teil der offiziellen Schnittstelle. Wir erzeugen also eine Klasse FooFactory, welche die folgenden Methoden enthält:

Name Rückgabetyp Beschreibung
static defaults() FooFactory Liefert ein einfachen FooFactory-Objekt.
static createFrom(Foo prototype) FooFactory Liefert ein FooFactory-Objekt mit Voreinstellungen wie beim übergebenen Foo-Objekt prototype.
static copy(FooFactory prototype) FooFactory Liefert ein FooFactory-Objekt mit dem Zustand des übergebenen FooFactory-Objekts prototype.
eigenschaft(wert) FooFactory Setzt Eigenschaft „eigenschaft“ des FooFactory-Objekts auf „wert“ und gibt es zurück.
create() Foo Gibt das erzeugte Foo-Objekt zurück.
applyTo(Bar container) void Nutzt die setFoo-Methode des Containers, um das erzeugte Foo-Objekt in container zu setzen.

Man sieht hier schön, dass Methoden zum Setzen von Eigenschaften, immer das eigene Objekt zurückgeben. Hiermit lässt sich eine schöne Verkettung erreichen, wie dies im GridDataFactory-Beispiel der Fall ist. Dieses Konzept wird auch als „Method Chaining“ bezeichnet.

Fazit: Elegant und nahezu stilkonform

Ich finde diesen Weg vor allem aus zwei Gründen sehr interessant: Zunächst die offensichtliche Leichtigkeit, mit der man auch komplexe Objekte erzeugen, duplizieren, konfigurieren und sogar in andere Objekte integrieren kann.

Der andere wichtige Punkt ist, dass man bestehende Coding-Guidelines nicht direkt verletzt. Die obige Beispielklasse Foo kann sauber auf Getter und Setter setzen, wie es etwa in Java üblich ist. Anstatt nun etwa bei den Settern Method-Chaining einzubauen, was klar gegen die üblichen Konzepte verstoßen würde, nimmt man einfach eine Factory hinzu. Diese ist zwar auch nicht mit den üblichen Konventionen vereinbar, aber das ist an dieser Stelle akzeptabel: Einerseits wurde sie direkt geschaffen, um bestehende Konventionen zu umgehen. Dies muss auch entsprechend dokumentiert und dem Nutzer klar gemacht werden. Andererseits ist das gesamte Projekt auch ohne die Factory nutzbar. Wer sich auf den „Konventionsbruch“ nicht einlassen möchte, erzeugt seine Objekte einfach weiterhin auf dem klassischen Weg.

Beispielquellen

Die SWT-Beispiele zur GridDataFactory sind aus der Eclipse-Dokumentation.

Ein Kommentar

  1. Hallo Sebastian,
    danke für den informativen Artikel.
    Jetzt habe ich das endlich mit den Factories verstanden! Gerne mehr von solchen Architektur-Stilen…
    Was ich mir vielleicht noch gewünscht hätte, wäre eine Beispiel-Implementierung oder ein Link dazu.
    Gruß DSIW

Kommentare sind geschlossen.