JS: Предметно-ориентированное проектирование
Теория: Dependency Injection Container
Каждый раз, когда в коде встречается подобная запись, мы уже можем сделать вывод, что полиморфизм включения обошёл этот код стороной. Подменить реализацию UserRepository не представляется возможным, ведь он прямо жестко закодирован в месте своего использования. Такая ситуация не всегда является проблемой, но, всё же, хотелось бы иметь возможность лёгкой подмены компонентов системы. Да и в тестах часто бывает нужно подменять реализации и использовать стабы.
В этой ситуации мы можем воспользоваться DIP (dependency inversion principle), то есть принципом инверсии зависимостей:
- Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Хотя он и звучит страшно, на практике, особенно в динамических языках, применять его проще простого. Грубо говоря, всё сводится к тому, что мы передаём зависимости снаружи, а клиентский код ими пользуется. Например так:
Как видите, теперь код не зависит от конкретной реализации репозитория. Так мы получаем преимущества от ООП. Только всегда будьте прагматиками. Инверсия ради инверсии, это так себе обоснование для усложнения. В реальности не так часто бывает нужна подмена, как об этом кричат в некоторых книжках, но всё же это важная тема.
Рядом с DIP всегда появляется словосочетание Dependency Injection или Внедрение Зависимостей — это набор способов, с помощью которых можно доставить зависимости. Кроме внедрения через параметры функции, выделяют следующие способы:
- Через конструктор
- Через сеттер
Кроме, собственно, полиморфизма, многим компонентам часто нужны готовые объекты, представляющие различные подсистемы программы. К таким компонентам могут относиться соединения с базой данных, доступы к кешам, любые компоненты с состоянием. Единственный способ получать к ним доступ без внедрения зависимостей — это использование глобальных переменных.
Многие действительно так и делают, более того, экосистемы некоторых языков подталкивают к таким подходам. Например, в Ruby очень часто объекты делаются глобальными переменными.
Из этой ситуации есть несколько хорошо изученных выходов.
Service Locator
Сервис-локатор (service locator) - это чуть более продвинутая альтернатива глобальным переменным. Этот паттерн подразумевает наличие одного глобального объекта, который и является сервис-локатором. В начале программы он инициализируется всеми нужными сервисами. В процессе жизни программы каждый компонент сам запрашивает у локатора нужные зависимости. Честно скажем, что этот подход так себе, но за неимением лучшего, может стать неплохим подспорьем.
DI Container
Самый продвинутый вариант называется Dependency Injection Container. При таком подходе контейнер становится центральной частью системы. С одной стороны он предоставляет интерфейс для описания всех сервисов и их зависимостей, с другой стороны сам занимается созданием графа объектов, попутно внедряя зависимости в те места, где они нужны. Такой подход особенно распространён в таких языках как Java или C#. Возможно, вы даже слышали такое название, как Spring Framework.
Ниже представлен один из вариантов того, как могли бы выглядеть части системы, использующей контейнер:
Как видите, класс не запрашивает никаких зависимостей сам, они внедряются через конструктор какой-то внешней системой.
bottlejs
bottlejs — это библиотека, которая позиционирует себя как DI Micro Container. В отличие от своих старших собратьев, она очень простая. С ее помощью достаточно легко собирать зависимости:
За сценой Bottlejs выполняет создание объектов и инъекцию нужных зависимостей в соответствии с конфигурацией. Пример выше работает так:
Такой способ работы подходит в простых случаях, когда зависимости это объекты без конфигурации. В более сложных случаях понадобится метод factory():
Количество возможных сервисов и способов их сбора никак не ограничено. Bottlejs универсальный инструмент. Он может собирать все что угодно, любым нужным способом.
Рекомендуемые программы
Завершено
0 / 8

