Was sind einige gemeinsame , reale Welt Beispiele der Builder - Muster zu verwenden? Was kauft es dir? Warum nicht einfach ein Factory-Muster verwenden?
java
design-patterns
builder
Charles Graham
quelle
quelle
Antworten:
Der Hauptunterschied zwischen einem Builder und einer Factory IMHO besteht darin, dass ein Builder nützlich ist, wenn Sie viele Dinge tun müssen, um ein Objekt zu erstellen. Stellen Sie sich zum Beispiel ein DOM vor. Sie müssen viele Knoten und Attribute erstellen, um Ihr endgültiges Objekt zu erhalten. Eine Factory wird verwendet, wenn die Factory problemlos das gesamte Objekt innerhalb eines Methodenaufrufs erstellen kann.
Ein Beispiel für die Verwendung eines Builders ist das Erstellen eines XML-Dokuments. Ich habe dieses Modell beim Erstellen von HTML-Fragmenten verwendet. Ich habe beispielsweise einen Builder zum Erstellen eines bestimmten Tabellentyps und die folgenden Methoden (Parameter werden nicht angezeigt). ::
Dieser Builder würde dann den HTML-Code für mich ausspucken. Dies ist viel einfacher zu lesen als eine große prozedurale Methode durchzugehen.
Schauen Sie sich das Builder-Muster auf Wikipedia an .
quelle
Im Folgenden sind einige Gründe aufgeführt, die für die Verwendung des Musters und des Beispielcodes in Java sprechen. Es handelt sich jedoch um eine Implementierung des Builder-Musters, das von der Viererbande in Entwurfsmustern behandelt wird . Die Gründe, warum Sie es in Java verwenden würden, gelten auch für andere Programmiersprachen.
Wie Joshua Bloch in Effective Java, 2. Ausgabe, feststellt :
Wir sind alle irgendwann auf eine Klasse mit einer Liste von Konstruktoren gestoßen, in der jeder Zusatz einen neuen Optionsparameter hinzufügt:
Dies wird als Teleskopkonstruktormuster bezeichnet. Das Problem bei diesem Muster besteht darin, dass es schwierig wird, sich die erforderliche Reihenfolge der Parameter sowie den gewünschten Konstruktor in einer bestimmten Situation zu merken , sobald die Konstruktoren 4 oder 5 Parameter lang sind .
Eine Alternative zum Telescoping Constructor Pattern ist das JavaBean Pattern, bei dem Sie einen Konstruktor mit den obligatorischen Parametern aufrufen und anschließend alle optionalen Setter aufrufen:
Das Problem hierbei ist, dass sich das Objekt, da es über mehrere Aufrufe erstellt wurde, möglicherweise während seiner Erstellung in einem inkonsistenten Zustand befindet. Dies erfordert auch viel zusätzlichen Aufwand, um die Gewindesicherheit zu gewährleisten.
Die bessere Alternative ist die Verwendung des Builder-Musters.
Beachten Sie, dass Pizza unveränderlich ist und dass sich alle Parameterwerte an einem einzigen Ort befinden . Da die Setter-Methoden des Builders das Builder-Objekt zurückgeben, können sie verkettet werden .
Dies führt zu Code, der leicht zu schreiben und sehr leicht zu lesen und zu verstehen ist. In diesem Beispiel kann die Erstellungsmethode geändert werden , um Parameter zu überprüfen, nachdem sie vom Builder in das Pizza-Objekt kopiert wurden, und eine IllegalStateException auszulösen, wenn ein ungültiger Parameterwert angegeben wurde. Dieses Muster ist flexibel und es ist einfach, in Zukunft weitere Parameter hinzuzufügen. Es ist wirklich nur nützlich, wenn Sie mehr als 4 oder 5 Parameter für einen Konstruktor haben. Das heißt, es könnte sich in erster Linie lohnen, wenn Sie den Verdacht haben, dass Sie in Zukunft weitere Parameter hinzufügen werden.
Ich habe mich stark mit diesem Thema aus dem Buch Effective Java, 2nd Edition von Joshua Bloch befasst. Um mehr über dieses Muster und andere effektive Java-Praktiken zu erfahren, empfehle ich es dringend.
quelle
new Pizza.Builder(12).cheese().pepperoni().bacon().build();
Pizza.Builder(12).cheese().pepperoni().bacon().build();
Sie müssten Ihren Code neu kompilieren oder haben unnötige Logik, wenn Sie nur einige Peperoni benötigen Pizza. Zumindest sollten Sie auch parametrisierte Versionen bereitstellen, wie sie ursprünglich von @Kamikaze Mercenary vorgeschlagen wurden.Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();
. Andererseits testen wir niemals Unit-Tests, oder?Betrachten Sie ein Restaurant. Die Erstellung des "heutigen Essens" ist ein Fabrikmuster, da Sie der Küche sagen, "Holen Sie mir das heutige Essen" und die Küche (Fabrik) anhand versteckter Kriterien entscheidet, welches Objekt generiert werden soll.
Der Builder wird angezeigt, wenn Sie eine benutzerdefinierte Pizza bestellen. In diesem Fall sagt der Kellner dem Koch (Baumeister): "Ich brauche eine Pizza; fügen Sie Käse, Zwiebeln und Speck hinzu!" Auf diese Weise macht der Builder die Attribute verfügbar, die das generierte Objekt haben sollte, verbirgt jedoch, wie sie festgelegt werden.
quelle
Die .NET StringBuilder-Klasse ist ein hervorragendes Beispiel für ein Builder-Muster. Es wird meistens verwendet, um eine Zeichenfolge in einer Reihe von Schritten zu erstellen. Das Endergebnis, das Sie bei ToString () erhalten, ist immer eine Zeichenfolge, aber die Erstellung dieser Zeichenfolge hängt davon ab, welche Funktionen in der StringBuilder-Klasse verwendet wurden. Zusammenfassend besteht die Grundidee darin, komplexe Objekte zu erstellen und die Implementierungsdetails der Erstellung zu verbergen.
quelle
b.append(...).append(...)
bevor man sie schließlich aufrufttoString()
. Zitat: infoq.com/articles/internal-dsls-javaFür ein Multithread-Problem mussten wir für jeden Thread ein komplexes Objekt erstellen. Das Objekt stellte die zu verarbeitenden Daten dar und kann sich je nach Benutzereingabe ändern.
Könnten wir stattdessen eine Fabrik benutzen? Ja
Warum haben wir nicht? Builder macht wohl mehr Sinn.
Fabriken werden zum Erstellen verschiedener Objekttypen verwendet, die denselben Basistyp haben (dieselbe Schnittstelle oder Basisklasse implementieren).
Builder erstellen immer wieder denselben Objekttyp, aber die Konstruktion ist dynamisch, sodass sie zur Laufzeit geändert werden kann.
quelle
Sie verwenden es, wenn Sie viele Optionen haben, mit denen Sie sich befassen müssen. Denken Sie an Dinge wie jmock:
Es fühlt sich viel natürlicher an und ist ... möglich.
Es gibt auch XML-Gebäude, String-Gebäude und viele andere Dinge. Stellen Sie
java.util.Map
sich vor, Sie hätten als Baumeister gearbeitet. Sie könnten solche Dinge tun:quelle
Beim Durchlaufen des Microsoft MVC-Frameworks habe ich mir Gedanken über das Builder-Muster gemacht. Ich bin auf das Muster in der ControllerBuilder-Klasse gestoßen. Diese Klasse gibt die Controller-Factory-Klasse zurück, die dann zum Erstellen einer konkreten Steuerung verwendet wird.
Der Vorteil, den ich bei der Verwendung des Builder-Musters sehe, besteht darin, dass Sie eine eigene Factory erstellen und in das Framework einbinden können.
@Tetha, es kann ein Restaurant (Framework) geben, das von einem Italiener geführt wird und Pizza serviert. Um Pizza zuzubereiten, verwendet der Italiener (Object Builder) Owen (Factory) mit einer Pizzabasis (Basisklasse).
Jetzt übernimmt der Inder das Restaurant vom Italiener. Indische Restaurant (Framework) Server Dosa anstelle von Pizza. Um dosa vorzubereiten, verwendet der Inder (Objektbauer) die Bratpfanne (Fabrik) mit einer Maida (Basisklasse).
Wenn Sie sich das Szenario ansehen, ist das Essen anders, die Art und Weise, wie das Essen zubereitet wird, ist anders, aber im selben Restaurant (unter demselben Rahmen). Das Restaurant sollte so gebaut sein, dass es chinesische, mexikanische oder jede andere Küche unterstützt. Mit dem Object Builder im Framework können Sie die gewünschte Küche einbinden. zum Beispiel
quelle
Ich mochte das Builder-Muster immer nicht als etwas Unhandliches, Aufdringliches und sehr oft von weniger erfahrenen Programmierern missbraucht. Es ist ein Muster, das nur dann Sinn macht, wenn Sie das Objekt aus einigen Daten zusammensetzen müssen, für die ein Schritt nach der Initialisierung erforderlich ist (dh wenn alle Daten erfasst wurden - tun Sie etwas damit). Stattdessen werden in 99% der Fälle Builder einfach zum Initialisieren der Klassenmitglieder verwendet.
In solchen Fällen ist es weitaus besser, einfach
withXyz(...)
Typensetter innerhalb der Klasse zu deklarieren und sie dazu zu bringen, einen Verweis auf sich selbst zurückzugeben.Bedenken Sie:
Jetzt haben wir eine ordentliche Einzelklasse, die ihre eigene Initialisierung verwaltet und fast die gleiche Arbeit wie der Builder erledigt, nur dass sie weitaus eleganter ist.
quelle
Ein weiterer Vorteil des Builders besteht darin, dass bei einer Factory immer noch eine gewisse Kopplung in Ihrem Code vorhanden ist, da die Factory alle Objekte kennen muss, die sie möglicherweise erstellen kann, damit sie funktioniert . Wenn Sie ein anderes Objekt hinzufügen, das erstellt werden könnte, müssen Sie die Factory-Klasse ändern, um ihn einzuschließen. Dies geschieht auch in der Abstract Factory.
Mit dem Builder hingegen müssen Sie nur einen neuen konkreten Builder für diese neue Klasse erstellen. Die Director-Klasse bleibt gleich, da sie den Builder im Konstruktor empfängt.
Es gibt auch viele Arten von Buildern. Kamikaze Mercenary`s gibt noch einen.
quelle
quelle
Aufbauend auf den vorherigen Antworten (Wortspiel beabsichtigt) ist Groovys eingebaute Unterstützung ein hervorragendes Beispiel aus der Praxis
Builders
.MarkupBuilder
StreamingMarkupBuilder
SwingXBuilder
Siehe Builder in der Groovy-Dokumentation
quelle
Ich habe Builder in einer eigenen Messaging-Bibliothek verwendet. Der Bibliothekskern empfing Daten von der Leitung und sammelte sie mit der Builder-Instanz. Nachdem Builder entschieden hatte, dass alles zum Erstellen einer Nachrichteninstanz erforderlich ist, erstellte Builder.GetMessage () eine Nachrichteninstanz unter Verwendung der von der Builder-Instanz gesammelten Daten Draht.
quelle
Schauen Sie sich InnerBuilder an, ein IntelliJ IDEA-Plugin, das dem Menü Generieren (Alt + Einfügen) eine Aktion 'Builder' hinzufügt, die eine innere Builder-Klasse generiert, wie in Effective Java beschrieben
https://github.com/analytically/innerbuilder
quelle
Als ich den Standard-XMLGregorianCalendar für mein XML verwenden wollte, um das Marshalling von DateTime in Java zu beanstanden, hörte ich viele Kommentare darüber, wie schwer und umständlich es war, es zu verwenden. Ich habe versucht, die XML-Felder in den xs: datetime-Strukturen zu steuern, um Zeitzone, Millisekunden usw. zu verwalten.
Deshalb habe ich ein Dienstprogramm zum Erstellen eines XMLGregorian-Kalenders aus einem GregorianCalendar oder java.util.Date entwickelt.
Aufgrund meines Arbeitsplatzes darf ich es nicht ohne rechtliche Genehmigung online teilen. Hier ist jedoch ein Beispiel dafür, wie ein Kunde es verwendet. Es abstrahiert die Details und filtert einige der Implementierungen von XMLGregorianCalendar, die für xs: datetime weniger verwendet werden.
Zugegeben, dieses Muster ist eher ein Filter, da es Felder im xmlCalendar als undefiniert festlegt, damit sie ausgeschlossen werden. Es "erstellt" es dennoch. Ich habe dem Builder problemlos andere Optionen hinzugefügt, um eine xs: date- und xs: time-Struktur zu erstellen und bei Bedarf auch Zeitzonen-Offsets zu bearbeiten.
Wenn Sie jemals Code gesehen haben, der XMLGregorianCalendar erstellt und verwendet, werden Sie sehen, wie dies die Manipulation erheblich vereinfacht.
quelle
Ein gutes Beispiel aus der Praxis ist das Testen Ihrer Klassen. Sie verwenden Sut-Builder (System Under Test).
Beispiel:
Klasse:
Prüfung:
Sut Builder:
quelle
CustomAuthenticationService
Klasse nur Setter hinzuzufügen ?