Bei der Verwendung des Builder Pattern gibt es immer wieder die Herausforderung,
dass man bei der Erzeugung der finalen Instanz die Gültigkeit aller vorangegangenen
Schritte überprüfen muss. Anders formuliert: Wurde eine gültige Kombination der
Methoden, die die Attribute setzen, verwendet? Dazu sehen wir uns das nachfolgende Beispiel einmal genauer an.
Wir haben zum einen die Klasse, von der Instanzen erzeugt werden sollen. Hier wurde auch gleich ein passender Builder generiert.
Dazu schreiben wir nun noch einen trivialen Validator.
Hier soll lediglich sichergestellt sein, dass die Values nicht alle gleichzeitig 0 sein werden. (Über den Sinn kann man natürlich beliebig lange diskutieren. 😉 )
Die Anwendung kann dann wie folgt aussehen.
Allerdings hat dies einige Schwächen. Hier muss man davon ausgehen, dass jeder beteiligte Entwickler es auch kennt und machen wird. Da wir über einen Builder verfügen, liegt es nahe, dies in die Methode build() zu verlegen.
Hierzu modifizieren wir den Builder.
Ich ändere hier den Rückgabetyp in eine Instanz der Klasse Optional um, um ein null zu vermeiden und nicht im Fehlerfall eine Exception werfen zu müssen.
Die Verwendung ändert sich damit nur geringfügig.
Nun wird es sicherlich nicht nur eine Regel geben, die es zu beachten gilt. Also ist der nächste Schritt, eine Menge von Validatoren zu verwenden. Erzeugen wir uns deshalb einen zweiten Validator und fügen diesen der Methode build() hinzu.
Da es sich allerdings um eine größere Menge von Validatoren handeln kann, ist hier eine Liste von Validatoren sinnvoller. Die Liste der Validatoren halten wir in dem jeweiligen Builder vor. Zusätzlich bekommt man die Möglichkeit, zur Laufzeit Validatoren hinzuzufügen und zu entfernen.
Da es sich bei dem Interface Validator um ein FunctionalInterface handelt, kann man natürlich auch mit Lamdas arbeiten.
Der nächste Schritt besteht nun darin, die Implementierung generischer zu gestalten.
Nennen wir die generische Builder-Implementierung CheckedBuilder.
Damit muss die generierte spezielle Builder-Implementierung nun nur noch minimal
angepasst werden. Der Builder muss von CheckedBuilder erben und in der Methode
build() die Methode checkAndGet(T value) aufrufen.
Die Verwendung der Instanz der Klasse Builder erfolgt dann genauso wie vorher.
Only one more thing
Wenn man nicht die Methode build() in dieser Form editieren möchte, kann man auch einen etwas anderen Weg gehen.
Die Methode build() kann auch in den CheckedBuilder verlegt werden, so dass man sie in dem generierten Builder löschen muss. Der CheckedBuilder wird abstract deklariert und bekommt die Methoden protected abstract T createInstance();
Damit verändert sich der Builder in der Form, dass man das Ganze sehr leicht in bestehende Templates einbauen kann. Die notwendigen Informationen zum Zeitpunkt des Generierens liegen vollständig vor. Nur leider nicht in der Form, dass man mittels Reflection innerhalb des CheckedBuilder darauf zugreifen kann.
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Blog-Autor*in
Sven Ruppert
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.