Це має на меті бути доповнюючою відповіддю Дока Брауна, а також відповісти на коментарі Дінаїза без відповіді, які все ще пов'язані з Питанням.
Те, що вам, мабуть, потрібно, - це основа для ведення DI. Наявність складних ієрархій не обов'язково означає поганий дизайн, але якщо вам доведеться вводити TimeFactory знизу вгору (від A до D) замість того, щоб вводити безпосередньо D, то, мабуть, щось не так у тому, як ви робите введення залежностей.
Одинокий? Ні, дякую. Якщо вам потрібен лише один екземпляр, щоб він поділився у вашому контексті програми (Використання контейнера IoC для DI, як Infector ++, потрібно просто прив’язати TimeFactory як єдиний екземпляр), ось приклад (C ++ 11, до речі, але так C ++. Можливо, перемістіть до C ++ 11? Ви безкоштовно отримуєте заявку на герметичність):
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>();
Тепер хорошим моментом контейнера IoC є те, що вам не потрібно передавати фабрику часу до D. Якщо вашому класу "D" потрібна фабрика часу, просто поставте заводський час як параметр конструктора для класу 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
як ви бачите, що ви вводите TimeFactory лише один раз. Як користуватися "A"? Дуже просто, кожен клас впорскується, будується в основному або знаходиться на заводі.
auto myA1 = ioc.build<A>(); //A is not "single" so many different istances
auto myA2 = ioc.build<A>(); //can live at same time
щоразу, коли ви створюєте клас A, він буде автоматично (лінива старість) вводитися всіма залежностями до D і D, буде вводитися TimeFactory, тому, викликаючи лише 1 метод, ви готові вашу повну ієрархію (і навіть складні ієрархії вирішуються таким чином видалення ВЕЛИКОГО коду пластини котла): Вам не потрібно викликати "new / delete", і це дуже важливо, оскільки ви можете відокремити логіку програми від коду клею.
D може створювати об'єкти часу з інформацією, яку може мати тільки D
Це просто, ваш TimeFactory має метод "create", тоді просто використовуйте інший підпис "create (params)" і все закінчено. Параметри, які не мають залежності, часто вирішуються таким чином. Це також знімає обов'язок введення речей, таких як "рядки" або "цілі числа", оскільки це просто додає додаткову плиту котла.
Хто створює кого? Контейнер IoC створює інстанції та фабрики, фабрики створюють решту (фабрики можуть створювати різні об'єкти з довільними параметрами, тож вам не потрібно штат для заводів). Ви все ще можете використовувати фабрики як обгортки для контейнера IoC: Взагалі Injectin IoC Container дуже поганий і такий же, як і для використання сервісного локатора. Деякі люди вирішили проблему, обернувши контейнер IoC з фабрикою (це не обов'язково, але це перевага, що ієрархія вирішується контейнером, і всі ваші фабрики стають ще простішими в обслуговуванні).
//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);
}
Також не зловживайте ін'єкційними залежностями, прості типи можуть бути просто членами класу або локальними змінними. Це здається очевидним, але я бачив, як люди вводять "std :: vector" лише тому, що існувала рамка DI, яка це дозволяла. Завжди пам’ятайте про закон Деметра: «Впорскуйте лише те, що вам справді потрібно ввести»