web-dev-qa-db-fra.com

Éviter les constructeurs avec de nombreux arguments

J'ai donc une usine qui crée des objets de différentes classes. Les classes possibles sont toutes dérivées d'un ancêtre abstrait. La fabrique a un fichier de configuration (syntaxe JSON) et décide quelle classe créer, selon la configuration de l'utilisateur.

Pour ce faire, la fabrique utilise boost :: property_tree pour l'analyse JSON. Il parcourt le ptree et décide quel objet concret créer.

Cependant, les objets-produits ont de nombreux champs (attributs). Selon la classe concrète, l'objet a environ 5-10 attributs, à l'avenir peut-être même plus.

Je ne sais donc pas à quoi devrait ressembler le constructeur des objets. Je peux penser à deux solutions:

1) Le constructeur du produit attend chaque attribut comme paramètre, ainsi, le constructeur se retrouvera avec 10+ paramètres. Ce sera laid et conduira à de longues lignes de code illisibles. Cependant, l'avantage est que l'usine peut analyser le JSON et appeler le constructeur avec les paramètres corrects. La classe de produit n'a pas besoin de savoir qu'elle a été créée en raison de la configuration JSON. Il n'a pas besoin de savoir qu'il y a du JSON ou une configuration impliquée du tout.

2) Le constructeur du produit n'attend qu'un seul argument, l'objet property_tree. Ensuite, il peut analyser les informations nécessaires. Si des informations dans la configuration sont manquantes ou hors limites, chaque classe de produit peut réagir correctement. L'usine n'a pas besoin de connaître les arguments nécessaires aux différents produits. L'usine n'a pas non plus besoin de savoir comment réagir en cas de mauvaise configuration. Et l'interface constructeur est unifiée et petite. Mais, comme inconvénient, le produit doit extraire les informations nécessaires du JSON, ainsi, il sait comment il est construit.

J'ai tendance à préférer la solution 2). Cependant, je ne suis pas sûr que ce soit un bon modèle d'usine. Il semble en quelque sorte mal de laisser le produit savoir qu'il est créé avec la configuration JSON. D'un autre côté, de nouveaux produits peuvent être introduits très simplement.

Des opinions à ce sujet?

10
lugge86

Je ne ferais pas l'option 2, car alors vous avez pour toujours compliqué la construction de votre objet avec l'analyse syntaxique de l'arborescence des propriétés. Si vous êtes à l'aise avec une classe qui a besoin d'autant de paramètres, vous devriez être à l'aise avec un constructeur qui a besoin d'autant de paramètres, c'est la vie!

Si votre principale préoccupation est la lisibilité du code, vous pouvez utiliser le modèle de générateur, il s'agit essentiellement du stopgap c ++/Java par manque d'arguments nommés. Vous vous retrouvez avec des choses qui ressemblent à ceci:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Alors maintenant, MyObject aura un constructeur privé, qui sera appelé dans Builder :: build. La bonne chose est que ce sera le seul endroit où vous aurez jamais à appeler un constructeur avec 10 paramètres. La fabrique d'arborescence de propriétés boost utilisera le générateur, et par la suite si vous voulez construire un MyObject directement ou à partir d'une source différente, vous passerez par le générateur. Et le générateur vous permet de nommer clairement chaque paramètre au fur et à mesure que vous le transmettez, afin qu'il soit plus lisible. Cela ajoute évidemment du passe-partout, vous devrez donc décider si cela en vaut la peine par rapport à simplement appeler le constructeur en désordre, ou regrouper certains de vos paramètres existants dans des structures, etc. Il suffit de lancer une autre option sur la table.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example

10
Nir Friedman

N'utilisez PAS la deuxième approche.

Ce n'est certainement pas la solution et ne ferait qu'instancier des cours dans votre logique métier, au lieu de la partie de votre application où se trouvent les usines.

Soit:

  • essayez de regrouper certains paramètres qui semblent représenter des choses similaires en objets
  • diviser la classe actuelle en plusieurs classes plus petites (avoir une classe de service avec 10 paramètres semble que la classe fasse trop de choses)
  • laissez-le tel quel, si votre classe n'est pas réellement un service, mais un objet de valeur à la place

À moins que l'objet que vous créez ne soit en fait une classe chargée de conserver les données, vous devez essayer de refactoriser le code et de diviser la grande classe en plus petites.

5
Andy

L'option 2 a presque raison.

Une option améliorée 2

Créez une classe "orientée vers l'avant" à qui il revient de prendre cet objet de structure JSON et de sélectionner les bits et d'appeler le ou les constructeurs d'usine. Il prend ce que fabrique l'usine et le donne au client.

  • L'usine n'a absolument aucune idée qu'un tel truc JSON existe même.
  • Le client n'a pas besoin de savoir de quels bits spécifiques l'usine a besoin.

Fondamentalement, le "front end" dit à les 2 Bobs: "Je traite avec les clients expurgé afin que les ingénieurs n'aient pas à le faire! J'ai des compétences humaines! " Pauvre Tom. S'il avait seulement dit "je dissocie le client de la construction. Ce résultat est une usine très cohésive"; il aurait pu garder son emploi.

Trop d'arguments?

Pas pour le client - communication frontale.

Front end - usine? Si ce n'est pas 10 paramètres, le mieux que vous puissiez faire est de différer le déballage, sinon la chose JSON d'origine, puis un DTO. Est-ce mieux que de passer le JSON à l'usine? Même différence que je dis.

Je considérerais fortement passer des paramètres individuels. Tenez-vous à l'objectif d'une usine propre et cohérente. Évitez les soucis de @ réponse DavidPacker.

Atténuer "trop ​​d'arguments"

  • Constructeurs d'usine ou de classe

    • prendre uniquement des arguments pour la construction d'une classe/d'un objet spécifique.
    • paramètres par défaut
    • paramètres facultatifs
    • arguments nommés
  • Regroupement d'arguments frontaux

    • Examine, évalue, valide, définit, etc. les valeurs d'argument guidées par les signatures de constructeur ci-dessus.
0
radarbob