Скажіть, у мене є клас, Foobar
який використовує (залежить від) клас Widget
. У добрі дні Widget
wolud буде оголошений як поле в Foobar
, або, можливо, як розумний вказівник, якщо потрібна поліморфна поведінка, і вона буде ініціалізована в конструкторі:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
І ми б все налаштували і зробили. На жаль, сьогодні діти Java посміятимуться з нас, як тільки вони це побачать, і це справедливо, як це пара Foobar
і Widget
разом. Рішення здається простим: застосуйте Dependency Injection, щоб вивести побудову залежностей з Foobar
класу. Але тоді C ++ змушує задуматися про власність залежностей. На думку приходять три рішення:
Унікальний покажчик
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
вимагає одноосібної власності на Widget
це, передане йому. Це має такі переваги:
- Вплив на продуктивність незначний.
- Це безпечно, оскільки
Foobar
контролює термін його експлуатаціїWidget
, тому переконайтеся, щоWidget
він раптово не зникне. - Це безпечно, що
Widget
не просочиться і буде зруйновано належним чином, коли більше не потрібно.
Однак це коштує:
- Він встановлює обмеження щодо використання
Widget
екземплярів, наприклад, неWidgets
можна використовувати виділений стек , неWidget
можна ділитися.
Спільний покажчик
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Це, мабуть, найближчий еквівалент Java та інших зібраних сміттям мов. Переваги:
- Більш універсальний, оскільки він дозволяє обмінюватися залежностями.
- Підтримує безпеку (точки 2 і 3)
unique_ptr
розчину.
Недоліки:
- Витрачає ресурси, коли не використовується спільний доступ.
- Все ще вимагає розподілу купи та забороняє виділяти стеки об'єкти.
Звичайний оглядовий вказівник
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Помістіть сирий покажчик всередину класу і перекладіть тягар власності на когось іншого. Плюси:
- Настільки просто, як це може вийти.
- Універсальний, приймає будь-яку
Widget
.
Мінуси:
- Більше не безпечно.
- Представляє інше підприємство, яке відповідає за право власності на
Foobar
іWidget
.
Деякі шалені метапрограмування шаблону
Єдина перевага, яку я можу придумати, - це те, що я зможу прочитати всі ті книги, на які я не знаходив часу, поки моє програмне забезпечення буває;)
Я схиляюся до третього рішення, оскільки воно є найбільш універсальним, все-таки треба керувати Foobars
, тому управління Widgets
- це проста зміна. Однак використання сирих покажчиків мене турбує, з іншого боку, розумне рішення вказівника почуває себе неправильно, оскільки вони змушують споживача залежності обмежувати, як ця залежність створюється.
Я щось пропускаю? Або просто ін'єкція залежності в C ++ не є тривіальною? Чи повинен клас володіти своїми залежностями або просто спостерігати за ними?
Foobar
це єдиний власник? У старому випадку це просто. Але проблема з DI, як я бачу, полягає в тому, що він відриває клас від побудови його залежностей, він також відокремлює його від власності на ці залежності (оскільки право власності пов'язане з будівництвом). У сміттєзбірних середовищах, таких як Java, це не проблема. У C ++ це так.
std::unique_ptr
це шлях. Ви можете використовуватиstd::move()
для передачі права власності на ресурс з верхнього діапазону до класу.