Ersetzen benannte Argumente das Buildermuster?

20

Hat das Builder-Muster bei Verwendung einer Sprache, die benannte und optionale Argumente unterstützt, keinen praktischen Nutzen mehr?

Erbauer:

new Builder(requiredA, requiredB).setOptionalA("optional").Build();

Optionale / benannte Argumente:

new Object(requiredA, requiredB, optionalA: "optional");
Paul Nikonowicz
quelle
3
Wie gehen Sie mit 20 optionalen Argumenten um? Es gibt kein Problem, das der Builder lösen muss, bis es groß wird. An dem Punkt, den Sie hier beschrieben haben, haben Sie zwei Konstruktoren (und ich würde keinen Builder für dieses kleine Problem bauen).
1
Sogar mit optionalen Argumenten - wenn der Konstruktor mehr als 2 Argumente enthält, bevorzuge ich die Verwendung eines Wertobjekts, um die Konfiguration zu kapseln. Gleiches gilt für Fluid Inerfaces und Builder: Alles, was größer als 3 ist, wird durch ein Wertobjekt ersetzt.
Thomas Junk

Antworten:

21

Builder sind am nützlichsten, wenn Ihr Objekt viele Argumente / Abhängigkeiten benötigt, um nützlich zu sein, oder wenn Sie viele verschiedene Möglichkeiten zum Erstellen des Objekts zulassen möchten.

Ich kann mir vorstellen, dass jemand Objekte in einem 3D-Spiel wie diesem "bauen" möchte:

// Just ignore the fact that this hypothetical god class is coupled to everything ever
new ObjectBuilder(x, y, z).importBlenderMesh("./meshes/foo")
                          .syncWithOtherPlayers(serverIP)
                          .compileShaders("./shaders/foo.vert", "./shaders/foo.frag")
                          .makeDestructibleRigidBody(health, weight)
                          ...

Ich würde argumentieren, dass dieses Beispiel mit den Builder-Methoden, die ich gerade erstellt habe, besser lesbar ist als mit optionalen Parametern:

new Object(x, y, z, meshType: MESH.BLENDER,
                    meshPath: "./meshes/foo",
                    serverToSyncWith: serverIP,
                    vertexShader: "./shaders/foo.vert",
                    physicsType: PHYSICS_ENGINE.RIGID_DESTRUCTIBLE,
                    health: health,
                    weight: weight)
                    ...

Insbesondere müssen die durch die Builder-Methodennamen implizierten Informationen durch noch mehr Parameter ersetzt werden, und es ist viel einfacher, einen Parameter in einer Gruppe eng verwandter Parameter zu vergessen. Tatsächlich fehlt der Fragment-Shader, aber das würden Sie nicht bemerken, wenn Sie nicht wüssten, dass Sie danach suchen.


Wenn für die Konstruktion Ihres Objekts nur ein bis fünf Argumente erforderlich sind, muss das Builder-Muster nicht einbezogen werden, unabhängig davon, ob Sie / optionale Parameter angegeben haben oder nicht.

Ixrec
quelle
Ich kaufe deine Argumente nicht. Wenn die Builder-Methodennamen so wunderbar sind, können Sie sie auch für die Parameternamen verwenden. Wenn Parameter in enger Beziehung zueinander stehen, platzieren Sie sie in einem kleinen Objektkonstruktor.
user949300
@ user949300 Ich denke, Sie haben den wichtigen Teil des Punktes übersehen, nämlich, dass die Builder-Methoden hier Beziehungen zwischen den Parametern beschreiben, die verloren gehen, wenn Sie nur eine Reihe von optionalen Parametern haben. Im Builder-Beispiel von Ixrec sind "Gesundheit" und "Gewicht" logischerweise Teil der Einstellungen für den zerstörbaren Körper, aber diese Beziehung geht in der optionalen Argumentversion verloren.
Jules
1
nicht, wenn einer der optionalen Parameter ist (Körper: neue DestructibleRigidBody (Gesundheit, Gewicht), ...)
Weyland Yutani
@Jules. Was Weyland gesagt hat - machen Sie einen kleinen namhaften Konstrukteur für Gewicht und Größe.
user949300
8

Zusätzlich zu dem, was Ixrec gesagt hat, ermöglichen es die Konstruktoren oder Methoden mit dem Namen parameters nicht, dass sich Ihr Objekt in einem Zustand befindet, in dem es noch geändert werden kann, bevor es erstellt wird. Dies ist das Schöne am Builder, wo Sie Teile seiner Konstruktion an verschiedene Methoden oder Klassen delegieren können:

var myThingBuilder = new ThingBuilder("table");
myThingBuilder.setAttribute(Attributes.Legs, 4);

inventoryManager.setPrices(myThingBuilder);

// inventory manager
var availableCheapestMaterial = getMaterial();
myThingBuilder.setMaterial(availableCheapestMaterial);

Grundsätzlich können Sie Ihren Builder auch so lange um Ihr System herumwerfen, bis das endgültige Objekt erstellt werden kann. Auf diese Weise können Sie das Wissen verringern, das Ihr Builder-Consumer benötigt.

Alpha
quelle
Ich verstehe deinen letzten Absatz nicht. Wenn Sie "Ihren Builder um das System werfen, bis es fertig ist", hat er viel zu viel Wissen über das System. Ihr ThingBuilder kennt sich mit Attributen aus, wird von InventoryManager auf mysteriöse Weise modifiziert und kennt sich mit Materialien aus. Sehen Sie nicht, wie das Wissen abnimmt.
user949300
@ user949300 von Denken , wie es sein würde , ohne den Bauherrn über das System unterwegs. Sie wären gezwungen, eine Klasse mit einem riesigen Fan-Out-Faktor zu haben, und das ist nur notwendig, um das Ding zu bauen. (Vorausgesetzt, Ihre Klasse konzentriert nicht das gesamte Wissen, ist dies das, was wir in erster Linie vermeiden wollten.) Wenn diese Klasse eine andere Verantwortung hat, erstellen Sie eine große Klasse, die S in SOLID bricht . Wenn das die einzige Verantwortung ist, machst du dich selbst zum ThingBuilder.
Alpha
1

Es hängt davon ab, was Sie mit dem Builder machen.

Wenn Sie den Builder nur zum Festlegen (und Variieren) von Objekteigenschaften und zum (Aufschieben) der Objekterstellung verwenden, kann er durch benannte Parameter ersetzt werden.

Wenn Sie den Builder ersetzen, kann es sein, dass Sie den von @Ixrec erwähnten Kompromiss zwischen Lesbarkeit und Verwendung eingegangen sind (oder nicht, dies hängt davon ab, was Sie mit dem Builder tun).

Wenn Ihr Builder jedoch mehr als nur die Eigenschaften enthält und jeder Konstruktionsschritt logisch ist, kann er nicht ersetzt werden.

MockBuilder ist ein Beispiel, bei dem es nicht durch benannte Parameter ersetzt werden kann. Von der Seite:

Erstellungsschritte für die Logik können nicht durch benannte Parameter ersetzt werden

rvazquezglez
quelle
-5

Das Builder-Muster ist wichtig, wenn Sie mit unveränderlichen Objekten arbeiten. Die Arbeit mit unveränderlichen Objekten hat viele Vorteile, insbesondere, wenn Sie Ihr Programm in einer gleichzeitigen Umgebung (z. B. Threads) stabiler ausführen.

codedabbler
quelle
3
Ja, Builder eignen sich hervorragend für die Arbeit mit komplexen unveränderlichen Objekten (oder sogar veränderlichen Objekten - Sie müssen sicherstellen, dass sich das Objekt in einem konsistenten Zustand befindet, bevor es verwendet werden kann). Das sei gesagt, new Integer(42), new BigDecimal("42.000")und new String("foobar")sind alle Konstrukteure zu immutables , die ... na ja, ein Baumeister unnötig für diese Fälle komplex sein würde. Ein Builder ist also nicht unbedingt für die Arbeit mit unveränderlichen Dateien erforderlich, wenn Konstruktoren genauso gut funktionieren können.
Ja, ein Konstruktor kann mit unveränderlichen Objekten verwendet werden, die nicht abgelehnt werden. Die ursprüngliche Frage war jedoch, ob das Builder-Muster abgesehen von den optionalen Argumenten eine praktische Verwendung hat, und meine Antwort war, dies zu klären.
Codedabbler
2
Die ursprüngliche Frage sagt nichts über unveränderliche Objekte aus. Es werden benannte Parameter und ihre Beziehung zu Buildern abgefragt. Bei Ihrer Antwort geht es um den Builder und seine Beziehung zu unveränderlichen Objekten. Es fällt mir schwer zu sehen, wie Ihre Antwort die Frage beantwortet.