Cela vise à être une réponse complémentaire à Doc Brown, et aussi à répondre aux commentaires sans réponse de Dinaiz qui sont toujours liés à la Question.
Ce dont vous avez probablement besoin, c'est d'un cadre pour faire DI. Avoir des hiérarchies complexes ne signifie pas nécessairement une mauvaise conception, mais si vous devez injecter une méthode ascendante TimeFactory (de A à D) au lieu d'injecter directement vers D, il y a probablement un problème avec la façon dont vous faites l'injection de dépendance.
Un singleton? Non merci. Si vous n'avez besoin que d'une seule istance pour la partager dans votre contexte d'application (l'utilisation d'un conteneur IoC pour DI comme Infector ++ nécessite simplement de lier TimeFactory en tant que istance unique), voici l'exemple (C ++ 11 soit dit en passant, mais C ++ donc. vers C ++ 11 déjà? Vous obtenez gratuitement une application sans fuite):
Infector::Container ioc; //your app's context
ioc.bindSingleAsNothing<TimeFactory>(); //declare TimeFactory to be shared
ioc.wire<TimeFactory>(); //wire its constructor
// if you want to be sure TimeFactory is created at startup just request it
// (else it will be created lazily only when needed)
auto myTimeFactory = ioc.buildSingle<TimeFactory>();
Maintenant, le bon point d'un conteneur IoC est que vous n'avez pas besoin de passer la fabrique de temps à D. si votre classe "D" a besoin de fabrique de temps, mettez simplement la fabrique de temps comme paramètre constructeur pour la classe D.
ioc.bindAsNothing<A>(); //declare class A
ioc.bindAsNothing<B>(); //declare class B
ioc.bindAsNothing<D>(); //declare class D
//constructors setup
ioc.wire<D, TimeFactory>(); //time factory injected to class D
ioc.wire<B, D>(); //class D injected to class B
ioc.wire<A, B>(); //class B injected to class A
comme vous le voyez, vous n'injectez TimeFactory qu'une seule fois. Comment utiliser "A"? Très simple, chaque classe est injectée, construite dans le principal ou à distance avec une usine.
auto myA1 = ioc.build<A>(); //A is not "single" so many different istances
auto myA2 = ioc.build<A>(); //can live at same time
chaque fois que vous créez la classe A, elle sera automatiquement (istiation paresseuse) injectée avec toutes les dépendances jusqu'à D et D sera injectée avec TimeFactory, donc en appelant seulement 1 méthode, vous avez votre hiérarchie complète prête (et même les hiérarchies complexes sont résolues de cette façon enlever BEAUCOUP de code de plaque de chaudière): Vous n'avez pas à appeler "nouveau / supprimer" et c'est très important car vous pouvez séparer la logique d'application du code de colle.
D peut créer des objets Time avec des informations que seul D peut avoir
C'est facile, votre TimeFactory a une méthode "create", alors utilisez simplement une signature différente "create (params)" et vous avez terminé. Les paramètres qui ne sont pas des dépendances sont souvent résolus de cette façon. Cela supprime également le devoir d'injecter des choses comme des "chaînes" ou des "entiers" car cela n'ajoute qu'une plaque de chaudière supplémentaire.
Qui crée qui? Le conteneur IoC crée des istances et des usines, les usines créent le reste (les usines peuvent créer différents objets avec des paramètres arbitraires, donc vous n'avez pas vraiment besoin d'un état pour les usines). Vous pouvez toujours utiliser les usines comme wrappers pour le conteneur IoC: en général, Injectin the IoC Container est très mauvais et revient à utiliser un localisateur de services. Certaines personnes ont résolu le problème en enveloppant le conteneur IoC avec une usine (ce n'est pas strictement nécessaire, mais a l'avantage que la hiérarchie est résolue par le conteneur et toutes vos usines deviennent encore plus faciles à entretenir).
//factory method
std::unique_ptr<myType> create(params){
auto istance = ioc->build<myType>(); //this code's agnostic to "myType" hierarchy
istance->setParams(params); //the customization you needed
return std::move(istance);
}
N'abusez pas non plus de l'injection de dépendances, les types simples peuvent simplement être des membres de classe ou des variables de portée locale. Cela semble évident mais j'ai vu des gens injecter "std :: vector" juste parce qu'il y avait un framework DI qui permettait cela. Rappelez-vous toujours la loi de Demeter: "Injectez uniquement ce dont vous avez vraiment besoin pour injecter"