«Не звони нам. Мы позвоним тебе сами.» — принцип Голливуда
Внедрение зависимости (Dependency Injection — DI) означает передачу (внедрение) одной или более зависимости какому-либо объекту (клиенту, компоненту) таким образом, что после внедрения эта зависимость становится частью состояния объекта. Это немного напоминает паттерн проектирования Стратегия, с той лишь разницей, что стратегия задаётся лишь однажды — в конструкторе. DI позволяет создавать более слабо-связанную архитектуру приложения, которая лучше поддаётся поддержке и тестированию.
Итак, ещё раз плюсы:
- Уменьшает связность компонент (отделяет бизнес-логику от создания объекта)
- Позволяет писать более поддерживаемый код (в одни и те же объекты мы легко можем внедрять разные зависимости)
- Позволяет писать более тестируемый код (мы можем легче использовать стабы и моки)
Без внедрения зависимости | С внедрением зависимости |
---|---|
|
|
Boost.DI это библиотека для реализации внедрения зависимости в С++. Для её использования нужно лишь включить в свой код один заголовочный файл. Цель библиотеки — упростить создание объектов с помощью автоматического внедрения зависимостей:
- Уменьшение количества «узких мест» в коде — никаких лишних фабрик, никакого создания объектов лишь в определённом порядке
- Упрощение поддержки кода — изменение сигнатуры конструктора не повлияет на конфигурацию DI
- Упрощение тестирование — автоматическая инъекция моков
- Более хороший контроль над тем, что и когда создаётся
- Лучшее понимание кода в плане иерархии объектов
Ручное внедрение зависимости | Boost.DI |
---|---|
|
|
Ещё раз — зачем использовать внедрение зависимости
С чего начать?
- Возьмите компилятор с поддержкой С++14 (Clang-3.4+, GCC-5.0+). Библиотека Boost не требуется
- Прочитайте этот документ
- Прочитайте учебник
- Прочитайте документацию
А вообще-то всё, что вам необходимо для работы с библиотекой, этой файл di.hpp
// main.cpp
#include "di.hpp"
int main() { }
$CXX -std=c++1y -I. main.cpp
Давайте предположим, что все примеры ниже включают boost/di.hpp и определяют пространство имён di как алиас boost::di.
#include <boost/di.hpp>
namespace di = boost::di;
//
struct i1 { virtual ~i1() = default; virtual void dummy1() = 0; };
struct i2 { virtual ~i2() = default; virtual void dummy2() = 0; };
struct impl1 : i1 { void dummy1() override { } };
struct impl2 : i2 { void dummy2() override { } };
struct impl : i1, i2 { void dummy1() override { } void dummy2() override { } };
Инжектор
Создание пустого инжектора | Тест |
---|---|
|
|
Привязки
Привязка интерфейса к реализации | Тест |
---|---|
|
|
Привязка нескольких интерфейсов к одной реализации | Тест |
---|---|
|
|
Привязка типа к значению, вычисляемому на этапе компиляции | Тест |
---|---|
|
|
Привязка типа к значению | Тест |
---|---|
|
|
Внедрение
Внедрение через обычный конструктор | Тест |
---|---|
|
|
Внедрение через аггрегацию | Тест |
---|---|
|
|
Внедрение через конструктор при наличии нескольких конструкторов (будет выбран тот, у которого наибольшее число параметров) |
Тест |
---|---|
|
|
Внедрение через конструктор при наличии вариантов выбора (использование BOOST_DI_INJECT) |
Тест |
---|---|
|
|
Внедрение через конструктор при наличии вариантов выбора (использование BOOST_DI_INJECT_TRAITS) |
Тест |
---|---|
|
|
Внедрение через конструктор при наличии вариантов выбора (использование di::ctor_traits) |
Тест |
---|---|
|
|
Аннотации
Внедрение через аннотированные конструкторы | Тест |
---|---|
|
|
Внедрение через аннотированные конструкторы с использованием именованных параметров |
Тест |
---|---|
|
|
Внедрение через аннотированные конструкторы с вынесением реализации конструктора |
Тест |
---|---|
|
|
Внедрение через аннотированные конструкторы с использованием di::ctor_traits |
Тест |
---|---|
|
|
Области видимости
Вывод области видимости (по-умолчанию) | Тест |
---|---|
|
|
Тип | Выведенная область видимости |
---|---|
T | unique |
T& | ошибка — должен быть связан как external |
const T& | unique (временный) |
T* | unique (передача владения) |
const T* | unique (передача владения) |
T&& | unique |
unique_ptr | unique |
shared_ptr | singleton |
weak_ptr | singleton |
Уникальная область видимости | Тест |
---|---|
|
|
Разделяемая область видимости (в пределах потока) | Тест |
---|---|
|
|
Синглтон (разделяем между потоками) | Тест |
---|---|
|
|
Область видимости сессии | Тест |
---|---|
|
|
Внешняя область видимости | Тест |
---|---|
|
|
Специальная область видимости | Тест |
---|---|
|
|
Модули
Модуль | Тест |
---|---|
|
|
Модуль, открывающий тип | Тест |
---|---|
|
|
Модуль, открывающий несколько типов | Тест |
---|---|
|
|
Модуль открытого типа с аннотацией | Тест |
---|---|
|
|
Провайдеры
«no throw»-провайдер | Тест |
---|---|
|
|
Политики
Определение политик конфигурации (дамп типов) | Тест |
---|---|
|
|
Определение политик конфигурации (развёрнутый дамп типов) | Тест |
---|---|
|
|
Политика «может быть сконструирован» | Тест |
---|---|
|
|
Производительность на рантайме
Окружение
- x86_64 Intel® Core(TM) i7-4770 CPU @ 3.40GHz GenuineIntel GNU/Linux
- clang++3.4 -O2 / gdb -batch -ex 'file ./a.out' -ex 'disassemble main'
Создание типа без привязок | Asm x86-64 (то же, что «return 0») |
---|---|
|
|
Создание типа с привязкой объекта | Asm x86-64 (то же, что «return 42») |
---|---|
|
|
Создание именованного типа | Asm x86-64 (то же, что «return 42») |
---|---|
|
|
Создание привязки интерфейса к реализации | Asm x86-64 (то же, что «make_unique») |
---|---|
|
|
Создание привязки интерфейса через модуль | Asm x86-64 (то же, что «make_unique») |
---|---|
|
|
Создание привязки интерфейса через открытый модуль | Asm x86-64 цена = вызов виртуального метода |
---|---|
|
|
Производительность на этапе компиляции
Окружение
- x86_64 Intel® Core(TM) i7-4770 CPU @ 3.40GHz GenuineIntel GNU/Linux
- clang++3.4 -O2
Заголовочный файл Boost.DI | Время [сек] |
---|---|
|
0.165 |
Легенда
- ctor = «голый» конструктор: c(int i, double d);
- inject = внедрение в конструктор: BOOST_DI_INJECT(c, int i, double d);
- all = все типы в модуле доступны: auto configure();
- exposed = один тип в модуле доступен: di::injector<c> configure();
- 4248897537 объектов создано
- 132 разных типа
- 10 модулей
- 1862039751439806464 объекта создано
- 200 разных типов
- 10 модулей
- 5874638529236910091 объект создан
- 310 разных типов
- 100 разных интерфейсов
- 10 модулей
Диагностические сообщения
Создание интерфейса без привязки к реализации | Сообщение об ошибке |
---|---|
|
|
Неоднозначность привязки | Сообщение об ошибке |
---|---|
|
|
Создание объекта без привязок при политике, требующей их |
Сообщение об ошибке |
---|---|
|
|
Неверный синтаксис аннотаций (NAMED вместо named) |
Сообщение об ошибке |
---|---|
|
|
Конфигурация
Макрос | Описание |
---|---|
|
|
Похожие библиотеки
Лицензия: Boost Software License, Version 1.0
Нюанс: библиотека пока не входит в Boost, а лишь находится в "инкубаторе".
Автор: tangro