Injection de dépendance en C++
C'est aussi une question que j'ai posée dans un commentaire dans L'un des Google talks de Miško Hevery qui traitait de l'injection de dépendance mais qui a été enterré dans les commentaires.
Je me demande comment l'étape usine / constructeur de câblage des dépendances ensemble peut fonctionner en C++.
C'est-à-dire que nous avons une classe A qui dépend de B. Le constructeur allouera B dans le tas, passera un pointeur à B dans le constructeur de A tout en allouant dans le tas et retournera un pointeur vers A.
Qui nettoie après? Est-il bon de laisser le constructeur nettoyer après que c'est fait? Cela semble être la méthode correcte car dans le discours, il est dit que le constructeur devrait configurer des objets qui devraient avoir la même durée de vie ou au moins les dépendances ont une durée de vie plus longue (j'ai aussi une question à ce sujet). Ce que je veux dire dans le code:
class builder {
public:
builder() :
m_ClassA(NULL),m_ClassB(NULL) {
}
~builder() {
if (m_ClassB) {
delete m_ClassB;
}
if (m_ClassA) {
delete m_ClassA;
}
}
ClassA *build() {
m_ClassB = new class B;
m_ClassA = new class A(m_ClassB);
return m_ClassA;
}
};
Maintenant, s'il y a une dépendance qui devrait durer plus longtemps que la durée de vie de l'objet, nous l'injectons dans (disons ClassC est-ce dépendance) je comprends que nous devrions changer la méthode de construction à quelque chose comme:
ClassA *builder::build(ClassC *classC) {
m_ClassB = new class B;
m_ClassA = new class A(m_ClassB, classC);
return m_ClassA;
}
Quelle est votre approche préférée?
8 réponses
Cette présentation concerne Java et l'injection de dépendances.
En C++, nous essayons Pas de passer des pointeurs bruts. C'est parce qu'un pointeur brut n'a pas de sémantique de propriété associée. Si vous n'avez pas de propriété, puis nous ne savons pas qui est responsable du nettoyage de l'objet.
Je trouve que la plupart du temps l'injection de dépendance se fait via des références en C++.
Dans les rares cas où vous devez utiliser des pointeurs, enveloppez-les dans std:: unique_ptr ou std::shared_ptr, selon la façon dont vous souhaitez gérer la propriété.
Si vous ne pouvez pas utiliser les fonctionnalités c++11, utilisez std::auto_ptr ou boost::shared_ptr.
Je voudrais également souligner que les styles de programmation C++ et Java sont maintenant si divergents que l'application du style d'un langage à l'autre conduira inévitablement à un désastre.
C'est intéressant, DI En C++ en utilisant des modèles:
Http://adam.younglogic.com/?p=146
Je pense que l'auteur fait les bons gestes pour ne pas traduire Java DI en C++ trop littéralement. La peine de la lire.
J'ai récemment été mordu par le bug DI. Je pense que cela résout beaucoup de problèmes de complexité, en particulier la partie automatisée. J'ai écrit un prototype qui vous permet d'utiliser DI d'une manière assez C++, ou du moins je le pense. Vous pouvez jeter un oeil à l'exemple de code ici: http://codepad.org/GpOujZ79
Les choses qui manquent évidemment: pas de portée, pas de liaison de l'interface à l'implémentation. Ce dernier est assez facile à résoudre, le premier, je n'en ai aucune idée.
Je serais reconnaissant si quelqu'un ici a une opinion sur le code.
Utilisez RAII.
Remettre un pointeur brut à quelqu'un revient à lui donner la propriété. Si ce n'est pas ce que vous voulez faire, vous devez leur donner une sorte de façade qui sait aussi comment nettoyer de l'objet en question.
Shared_ptr peut le faire; le deuxième argument de son constructeur peut être un objet de fonction qui sait comment supprimer l'objet.
En C++, normalement, lorsque vous avez bien fait les choses, vous n'avez pas besoin d'écrire des destructeurs dans la plupart des cas. Vous devez utiliser des pointeurs intelligents pour supprimer les choses automatiquement. Je pense que builder ne ressemble pas au propriétaire des instances ClassA et ClassB. Si vous n'aimez pas utiliser des pointeurs intelligents, vous devriez penser à la durée de vie des objets et à leurs propriétaires.
Les choses se compliquent si vous ne vous réglez pas une fois pour toutes sur la question de la propriété. Vous devrez simplement décider dans votre implémentation s'il est possible que les dépendances vivent plus longtemps que les objets dans lesquels elles sont injectées.
Personnellement, je dirais non: l'objet dans lequel la dépendance est injectée va nettoyer après. Essayer de le faire via le constructeur signifie que le constructeur devra vivre plus longtemps que la dépendance et l'objet dans lequel il se trouve injecter. Cela provoque plus de problèmes qu'il n'en résout, à mon avis, parce que le constructeur ne sert plus de but utile après la construction avec l'injection de dépendance a été terminée.
Sur la base de ma propre expérience, il est préférable d'avoir des règles de propriété claires. Pour les petits objets concrets, il est préférable d'utiliser la copie directe pour éviter la dépendance croisée.
Parfois, la dépendance croisée est inévitable, et il n'y a pas de propriété claire. Par exemple, (M) les instances A possèdent (n) Les instances B, et certaines instances B peuvent être détenues par plusieurs As. Dans ce cas, la meilleure approche consiste à appliquer le comptage de référence à B, de la même manière que le comptage de référence COM. Toutes les fonctions qui prennent la possession de B* doit d'abord augmenter le nombre de références et le diminuer lors de la libération de la possession.
J'évite également d'utiliser boost:: shared_ptr car il crée un nouveau type (shared_ptr et B * deviennent deux types distincts). J'ai trouvé que cela apporte plus de maux de tête quand j'ajoute des méthodes.
Vous pouvez également vérifier le injection de dépendance FFEAD. Il fournit DI sur les lignes de Spring pour JAVA et a une façon non envahissante de traiter les choses. Il a également beaucoup d'autres fonctionnalités importantes comme le Support AJAX intégré,la réflexion,la sérialisation,L'interpréteur C++, Les composants métier pour C++,ORM,la messagerie,les services Web,les Pools de threads et un serveur d'applications qui prend en charge toutes ces fonctionnalités.