Questo mira ad essere una risposta complementare a Doc Brown e anche a rispondere ai commenti senza risposta di Dinaiz che sono ancora correlati alla domanda.
Ciò di cui probabilmente hai bisogno è un framework per fare DI. Avere gerarchie complesse non significa necessariamente cattiva progettazione, ma se devi iniettare una TimeFactory dal basso (da A a D) invece di iniettare direttamente in D, probabilmente c'è qualcosa che non va nel modo in cui stai facendo l'iniezione di dipendenza.
Un singleton? No grazie. Se hai bisogno di una sola istanza per renderla condivisa nel contesto dell'applicazione (l'utilizzo di un contenitore IoC per DI come Infector ++ richiede solo di legare TimeFactory come istanza singola), ecco l'esempio (C ++ 11 a proposito, ma così C ++. Forse spostare su C ++ 11? Ottieni un'applicazione Leak-Free gratuitamente):
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>();
Ora il punto positivo di un contenitore IoC è che non è necessario passare il time factory a D. se la classe "D" richiede time factory, basta inserire time factory come parametro del costruttore per 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
come vedi iniettare TimeFactory solo una volta. Come usare "A"? Molto semplice, ogni classe viene iniettata, costruita principalmente o istanziata da una fabbrica.
auto myA1 = ioc.build<A>(); //A is not "single" so many different istances
auto myA2 = ioc.build<A>(); //can live at same time
ogni volta che crei la classe A verrà automaticamente iniettato (istante pigro) iniettato con tutte le dipendenze fino a D e D verrà iniettato con TimeFactory, quindi chiamando solo 1 metodo avrai pronta la tua gerarchia completa (e anche le gerarchie complesse vengono risolte in questo modo rimozione di MOLTO codice della piastra della caldaia): non è necessario chiamare "nuovo / elimina" e questo è molto importante perché è possibile separare la logica dell'applicazione dal codice colla.
D può creare oggetti Time con informazioni che solo D può avere
È facile, il tuo TimeFactory ha un metodo "create", quindi usa una firma diversa "create (params)" e il gioco è fatto. I parametri che non sono dipendenze vengono spesso risolti in questo modo. Questo rimuove anche il dovere di iniettare cose come "stringhe" o "numeri interi" perché aggiungono solo una piastra aggiuntiva per la caldaia.
Chi crea chi? Il contenitore IoC crea istanze e fabbriche, le fabbriche creano il resto (le fabbriche possono creare oggetti diversi con parametri arbitrari, quindi non è necessario uno stato per le fabbriche). Puoi ancora usare le fabbriche come involucri per il contenitore IoC: in genere l'iniezione nel contenitore IoC è molto negativa ed è la stessa cosa che usare un localizzatore di servizi. Alcune persone hanno risolto il problema avvolgendo il contenitore IoC con una fabbrica (questo non è strettamente necessario, ma ha il vantaggio che la gerarchia è risolta dal contenitore e tutte le fabbriche diventano ancora più facili da mantenere).
//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);
}
Inoltre, non abusare dell'iniezione di dipendenza, i tipi semplici possono essere solo membri della classe o variabili con ambito locale. Questo sembra ovvio, ma ho visto la gente iniettare "std :: vector" solo perché c'era un framework DI che lo permetteva. Ricorda sempre la legge di Demetra: "Iniettare solo ciò di cui hai veramente bisogno per iniettare"