Это делается для того, чтобы служить дополнительным ответом Доку Брауну, а также отвечать на оставшиеся без ответа комментарии Динаиза, которые все еще имеют отношение к Вопросу.
То, что вам, вероятно, нужно, это рамки для DI. Наличие сложных иерархий не обязательно означает плохой дизайн, но если вам нужно внедрить восходящую TimeFactory (от A до D) вместо прямой инъекции в D, то, вероятно, что-то не так с тем, как вы делаете Dependency Injection.
Синглтон? Нет, спасибо. Если вам нужен только один istance, сделайте его общим для контекста вашего приложения (использование контейнера IoC для DI, такого как Infector ++, требует просто привязать TimeFactory как один istance), вот пример (кстати, 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 только один раз. Как использовать «А»? Очень просто, каждый класс вводится, строится в основном или заводится с завода.
auto myA1 = ioc.build<A>(); //A is not "single" so many different istances
auto myA2 = ioc.build<A>(); //can live at same time
каждый раз, когда вы создаете класс A, он будет автоматически (ленивый istantiation) вводиться со всеми зависимостями вплоть до D, а D будет добавляться с TimeFactory, поэтому, вызывая только 1 метод, вы получаете готовую иерархию (и даже сложные иерархии решаются таким образом). удаление БОЛЬШОГО кода пластины котельной): вам не нужно называть «новый / удалить», и это очень важно, потому что вы можете отделить логику приложения от кода клея.
D может создавать объекты времени с информацией, которую может иметь только D
Это просто, у вашей TimeFactory есть метод «создать», затем просто используйте другую подпись «создать (параметры)», и все готово. Параметры, которые не являются зависимостями, часто разрешаются таким образом. Это также избавляет от необходимости вводить такие вещи, как «строки» или «целые числа», потому что это просто добавляет дополнительную пластину котла.
Кто кого создает? Контейнер IoC создает экземпляры и фабрики, фабрики создают остальное (фабрики могут создавать разные объекты с произвольными параметрами, поэтому вам не нужно состояние для фабрики). Вы по-прежнему можете использовать фабрики как оболочки для IoC-контейнера: вообще говоря, Injectin для IoC-контейнера очень плох и аналогичен использованию сервисного локатора. Некоторые люди решили проблему, упаковав Контейнер 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, которая позволяла это делать. Всегда помните закон Деметры: «Вводите только то, что вам действительно нужно ввести»