Les arguments nommés remplacent-ils le modèle de générateur?

20

Lorsque vous utilisez un langage qui prend en charge les arguments nommés et facultatifs, le modèle de générateur n'a-t-il plus d'utilité pratique?

Constructeur:

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

Arguments facultatifs / nommés:

new Object(requiredA, requiredB, optionalA: "optional");
Paul Nikonowicz
la source
3
Comment gérez-vous 20 arguments facultatifs? Il n'y a pas de problème que le générateur doit résoudre jusqu'à ce qu'il devienne grand. Au point que vous avez décrit ici, vous avez deux constructeurs (et je ne construirais pas de constructeur pour ce petit problème).
1
Même avec des arguments facultatifs - si le constructeur a plus de 2 arguments, je préfère utiliser un objet valeur pour encapsuler la configuration. Il en va de même pour les inerfaces fluides et le générateur: tout ce qui est supérieur à 3 serait remplacé par un objet de valeur.
Thomas Junk

Réponses:

21

Les constructeurs sont plus utiles lorsque votre objet a besoin de nombreux arguments / dépendances pour être utile, ou si vous souhaitez autoriser de nombreuses façons différentes de construire l'objet.

Du haut de ma tête, je peux imaginer que quelqu'un pourrait vouloir "construire" des objets dans un jeu 3D comme celui-ci:

// 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)
                          ...

Je dirais que cet exemple est plus lisible avec les méthodes de générateur que j'ai inventées tout à l'heure qu'avec des paramètres facultatifs:

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)
                    ...

En particulier, les informations impliquées par les noms de méthode de générateur doivent être remplacées par encore plus de paramètres, et il est beaucoup plus facile d'oublier un paramètre dans un groupe de paramètres étroitement liés. En fait, le fragment shader est manquant, mais vous ne le remarquerez pas à moins de savoir le chercher.


Bien sûr, si votre objet ne prend qu'un à cinq arguments à construire, il n'est pas nécessaire d'impliquer le modèle de générateur, que vous ayez ou non nommé des paramètres facultatifs.

Ixrec
la source
Je n'achète pas vos arguments. Si les noms de méthode de générateur sont si merveilleux, vous pouvez également les utiliser pour les noms de paramètres. Si les paramètres sont étroitement liés, placez-les dans un petit constructeur d'objet.
user949300
@ user949300 Je pense que vous avez manqué la partie importante du point, c'est que les méthodes de construction ici décrivent les relations entre les paramètres qui se perdent si vous avez juste un tas de paramètres optionnels. Dans l'exemple du générateur d'Ixrec, «santé» et «poids» font clairement partie logiquement des paramètres du corps destructible, mais cette relation get l se perd dans la version d'argument facultative.
Jules
1
pas si l'un des paramètres optionnels est (corps: nouveau DestructibleRigidBody (santé, poids), ...)
Weyland Yutani
@Jules. Ce que Weyland a dit - faites un petit constructeur bien nommé pour le poids et la taille.
user949300
8

En plus de ce qu'Ixrec a dit, les constructeurs ou la méthode nommée paramètres ne vous permettront pas d'avoir votre objet dans un état à construire dans lequel il peut encore être modifié avant de le construire. C'est la beauté du Builder, où vous pouvez déléguer des parties de sa construction à différentes méthodes ou classes:

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

inventoryManager.setPrices(myThingBuilder);

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

Fondamentalement, vous pouvez également lancer votre générateur autour de votre système jusqu'à ce qu'il soit prêt à créer l'objet final, ce qui vous permet de réduire la quantité de connaissances dont votre constructeur-consommateur a besoin.

Alpha
la source
Je ne comprends pas votre dernier paragraphe. Si vous "jetez votre constructeur autour du système jusqu'à ce qu'il soit prêt", il a beaucoup trop de connaissances sur le système. Votre ThingBuilder connaît les attributs, est mystérieusement modifié par InventoryManager et connaît les matériaux. Je ne vois pas comment cela diminue les connaissances.
user949300
@ user949300 Imaginez comment cela se passerait sans que le constructeur ne parcourt le système. Vous seriez obligé d'avoir une classe avec un énorme facteur de fan-out, et c'est juste nécessaire pour construire la Chose. (Bien sûr, en supposant que votre classe ne concentre pas toutes les connaissances, c'est ce que nous voulions éviter en premier lieu.) Maintenant, si cette classe a une autre responsabilité, vous créez une classe énorme, brisant S dans SOLID . Si c'est la seule responsabilité, vous vous faites un ThingBuilder.
Alpha
1

Cela dépend de ce que vous faites avec le constructeur.

Si vous utilisez le générateur uniquement pour définir (et faire varier) les propriétés des objets et (différer) la création d'objets, il peut être remplacé par des paramètres nommés.

En remplaçant le générateur, vous pouvez avoir le compromis lisibilité / utilisation que @Ixrec a mentonné (ou ne pas l'avoir, cela dépend de ce que vous faites avec le générateur).

Cependant, si votre constructeur fait plus que simplement conserver les propriétés et que chaque étape de construction implique une logique, elle ne peut pas être remplacée.

MockBuilder est un exemple où il ne peut pas être remplacé par des paramètres nommés. Depuis la page:

La logique des étapes de création ne peut pas être remplacée par des paramètres nommés

rvazquezglez
la source
-5

Le modèle de générateur est essentiel lorsque vous travaillez avec des objets immuables. Il y a beaucoup d'avantages à travailler avec des objets immuables, en particulier pour rendre votre programme plus robuste à exécuter dans un environnement simultané (c'est-à-dire des threads)

codedabbler
la source
3
Oui, les constructeurs sont parfaits pour travailler avec des objets immuables complexes (ou même mutables - vous devez vous assurer que l'objet est dans un état cohérent avant de pouvoir être utilisé). Cela dit, new Integer(42), new BigDecimal("42.000")et new String("foobar")sont tous les constructeurs à immutables que ... eh bien, un constructeur serait complexe inutilement pour ces cas. Un constructeur n'est donc pas essentiel pour travailler avec immuable alors que les constructeurs peuvent tout aussi bien fonctionner.
Oui, un constructeur peut être utilisé avec des objets immuables, ce qui n'est pas refusé. Mais la question initiale était de savoir si le modèle Builder avait une utilité pratique en dehors des arguments facultatifs et ma réponse a été de clarifier cela.
codedabbler
2
La question d'origine ne dit rien sur les objets immuables. Il pose des questions sur les paramètres nommés et leur relation avec les constructeurs. Votre réponse concerne le constructeur et sa relation avec les objets immuables. J'ai du mal à voir comment votre réponse répond à la question.