6
votes

Trop de constructeur arguments pour le motif de conception d'injection / héritage de dépendance

J'ai donc décidé d'utiliser le modèle de conception d'usine avec une injection de dépendance.

class ClassA
{        
    Object *a, *b, *c;
    public:
    ClassA(Object *a, Object *b, Object *c) :
    a(a), b(b), c(c) {}
};

class ClassB : public ClassA
{        
    Object *d, *e, *f;
    public:
    ClassB(Object *a, Object *b, Object *c, Object *d, Object *e, Object *f) :
    ClassA(a, b, c), d(d), e(e), f(f) {}
};


5 commentaires

Mon approche générale consiste à éviter le nombre de héritiers autant que possible et à essayer de garder chaque classe axée sur une responsabilité unique. Pourriez-vous construire classa séparément, puis initialiser classb avec une référence à cela, plutôt que de les coupler étroitement par héritage?


Mike Seymour a raison. Préfère une relation A-A-A (composition) au plus étroitement couplé est-une relation (héritage). Peut-être que si vous expliquez ce que vous voulez faire, nous pouvons vous dire à quel point il est préférable de l'accomplir?


Je ne vois aucun problème majeur avec héritage. Il devrait être utilisé partout où il est nécessaire. Il ne devrait pas être abusé comme tout le reste.


@Kirillkobelev: Je suis tout à fait d'accord; Il devrait être évité à moins d'être nécessaire.


Qu'est-ce qui constitue «trop d'arguments»? Lors de l'utilisation du CIO, le conteneur prend soin d'eux et cela ne se produit que lorsque vous Héritage d'abus ou avez un «objet de Dieu» qui fait tout et dépend de l'univers.


3 Réponses :


1
votes

Ceci est l'un des problèmes C ++ (si cela peut être appelé un problème). Il n'a pas de solution autre que d'essayer de conserver le nombre de paramètres de la CTOR minimale.

Une des approches utilise les accessoires STRIT comme: P>

struct PropsA
{
    Object *a, *b, *c;
};

class ClassA
{
    ClassA(PropsA &props, ... other params);
};


8 commentaires

Dans la plupart des cas, il serait logique de faire propsa une classe à part entière en appuyant également des méthodes associées. Sinon, vous venez de vous retrouver avec une structure de données qui n'est pas très orientée objet.


J'ai mis les exigences de l'application, de la rapidité du développement et de la clarté du code de loin devant les affaires fantaisistes d'être "orienté objet".


OOP n'est pas "fantaisie", mais c'est un choix que vous prenez ou laissez. Je vais bien avec quelqu'un qui le fait juste de la manière procédurale, en utilisant des structures et des fonctions, mais si vous envisagez d'utiliser des classes, il est payant de mettre des efforts pour le faire correctement, sinon vous vous retrouvez dans un désordre où la moitié de votre code est orienté objet et le reste est procédural - je ne pense pas que cela conduit beaucoup de clarté.


Le OOP lui-même n'est pas fantaisiste. C'est un ensemble de méthodes extrêmement utiles. Seulement il est nécessaire de penser à quels éléments de cet ensemble sont utiles dans chaque cas, et seulement après avoir pensé à les utiliser. Si quelque chose n'a pas de méthodes raisonnables et / ou de vivre pendant une courte période - il devrait être une structure. Et il est juste que ce soit une structure. Cela ne rend pas le code moins orienté objet. OOP juste pour OOP - c'est des trucs de fantaisie inutiles.


Je suis totalement d'accord que les structures ont leur place, mais c'est pourquoi j'ai dit "dans la plupart des cas, ..." - Dans la plupart des cas, lorsqu'un constructeur prend des arguments "trop ​​nombreux", c'est un signe que la classe fait trop et pourrait être refoulé dans des classes plus petites.


Cela peut être que j'étais trop dur. Désolé. "Dans la plupart des cas" me semblait "toujours avec de rares exceptions près". "C'est un signe que la classe fait trop" - oui, cela peut être un signe. Seulement avant de refactoriser qu'il reste nécessaire de procéder à une évaluation si ce morceau de code est le pire de l'ensemble du projet ou non. Très probablement que autre lieu a besoin d'une attention plus urgente.


Pas de soucis. En effet, je suis un peu puriste OO, et lorsque quelqu'un marge une question avec "oop" ou "motifs de conception", je suis naturellement enclin à discuter de la bonne voie plutôt que la solution la plus rapide. :)


Cette méthode ressemble à "modèle d'objet de paramètre" et bonne option. Discussion similaire: Stackoverflow.com/questions/5551640/...



6
votes

Setter ne sont pas recommandés pour de telles choses car elles produisent un objet partiellement construit qui est sujette très erronée. Un modèle commun pour la construction d'un objet nécessitant de nombreux paramètres est l'utilisation du constructeur. La responsabilité de Classbbuilder est de créer des objets de classe. Vous faites du Constructeur Classb Private et permet à seulement un constructeur de l'appeler à l'aide de la relation d'amis. Maintenant, le constructeur peut sembler d'une manière ou d'une autre comme ça xxx pré>

et que vous utilisez le constructeur aime ceci: p>

ClassB* b = ClassBBuilder().setName('alice').setSurname('Smith').build();


3 commentaires

Cela peut être un peu plus agréable que les configurateurs, mais vous retardez efficacement la vérification de suffisamment d'informations sont présentes pour construire un objet à l'exécution du temps. Je ne considère pas qu'une amélioration sur un constructeur uni prenant quelques arguments.


Si vous souhaitez une mise en œuvre du temps compilé, vous pouvez toujours bénéficier du constructeur. C'est le cas lorsqu'un constructeur de classe prend de nombreux paramètres facultatifs avec des valeurs par défaut. Ensuite, vous pouvez utiliser les setters de Builder pour définir ces paramètres facultatifs et avoir la méthode de construction () prendre tous les paramètres requis. Cela vous donnerait la compilation de l'exécution du temps que tous les paramètres requis sont dépassés, mais seront plus lisibles et moins enclins à un appel à un constructeur qui prend de nombreux paramètres certains d'entre eux ont besoin d'une partie d'entre elles en option avec des valeurs par défaut.


J'aime cette approche. Le code pour un client construit l'objet est beaucoup plus propre. Bien que c'est vrai que vous perdiez la vérification de l'heure de la compilation, la fonction Build () permet toujours de vérifier votre code de validation dans votre code.



1
votes

Je pense que ce que vous décrivez n'est pas un problème dans C ++ - en fait, C ++ reflète les dépendances exprimées de votre conception assez bien:

  1. Pour construire un objet de type classa , vous devez avoir trois instances objet ( A , b et c ).
  2. Pour construire un objet de type Classb , vous devez également avoir trois instances objet ( d , E et f ).
  3. Chaque objet de type classb peut être traité comme un objet de type classa .

    Cela signifie que pour la construction d'un objet de type Classb , vous devez fournir trois objets objet nécessaires à la mise en oeuvre du classa interface, puis trois autres pour la mise en oeuvre de l'interface classb .

    Je crois que le problème réel est votre conception. Vous pouvez envisager différentes approches pour résoudre ce problème:

    1. Ne laissez pas classb hériter classa . Peut être une option selon que vous avez besoin d'un accès homogène aux objets de l'un ou l'autre type (par exemple, car vous avez une collection de classa * et cette collection pourrait également contenir des pointeurs sur Classb < / code>).
    2. Recherchez des objets qui apparaissent toujours ensemble. Comme - peut-être les deux premiers objets passés à l'un ou l'autre constructeur ( a et b ou d et E ) représentent certains sorte de paire. Peut-être qu'un identifiant d'objet ou similaire? Dans ce cas, il peut être utile d'introduire un résumé dédié (lecture: type) pour cela.

0 commentaires