0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Каждый раз, когда в коде встречается подобная запись, мы уже можем сделать вывод, что полиморфизм включения обошёл этот код стороной. Подменить реализацию UserRepository не представляется возможным, ведь он прямо жестко закодирован в месте своего использования. Такая ситуация не всегда является проблемой, но, всё же, хотелось бы иметь возможность лёгкой подмены компонентов системы. Да и в тестах часто бывает нужно подменять реализации и использовать стабы.</p>
1
<p>Каждый раз, когда в коде встречается подобная запись, мы уже можем сделать вывод, что полиморфизм включения обошёл этот код стороной. Подменить реализацию UserRepository не представляется возможным, ведь он прямо жестко закодирован в месте своего использования. Такая ситуация не всегда является проблемой, но, всё же, хотелось бы иметь возможность лёгкой подмены компонентов системы. Да и в тестах часто бывает нужно подменять реализации и использовать стабы.</p>
2
<p>В этой ситуации мы можем воспользоваться DIP (dependency inversion principle), то есть принципом инверсии зависимостей:</p>
2
<p>В этой ситуации мы можем воспользоваться DIP (dependency inversion principle), то есть принципом инверсии зависимостей:</p>
3
<ul><li>Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.</li>
3
<ul><li>Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.</li>
4
<li>Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.</li>
4
<li>Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.</li>
5
</ul><p>Хотя он и звучит страшно, на практике, особенно в динамических языках, применять его проще простого. Грубо говоря, всё сводится к тому, что мы передаём зависимости снаружи, а клиентский код ими пользуется. Например так:</p>
5
</ul><p>Хотя он и звучит страшно, на практике, особенно в динамических языках, применять его проще простого. Грубо говоря, всё сводится к тому, что мы передаём зависимости снаружи, а клиентский код ими пользуется. Например так:</p>
6
<p>Как видите, теперь код не зависит от конкретной реализации репозитория. Так мы получаем преимущества от ООП. Только всегда будьте прагматиками. Инверсия ради инверсии, это так себе обоснование для усложнения. В реальности не так часто бывает нужна подмена, как об этом кричат в некоторых книжках, но всё же это важная тема.</p>
6
<p>Как видите, теперь код не зависит от конкретной реализации репозитория. Так мы получаем преимущества от ООП. Только всегда будьте прагматиками. Инверсия ради инверсии, это так себе обоснование для усложнения. В реальности не так часто бывает нужна подмена, как об этом кричат в некоторых книжках, но всё же это важная тема.</p>
7
<p>Рядом с<em>DIP</em>всегда появляется словосочетание<em>Dependency Injection</em>или<em>Внедрение Зависимостей</em>- это набор способов, с помощью которых можно доставить зависимости. Кроме внедрения через параметры функции, выделяют следующие способы:</p>
7
<p>Рядом с<em>DIP</em>всегда появляется словосочетание<em>Dependency Injection</em>или<em>Внедрение Зависимостей</em>- это набор способов, с помощью которых можно доставить зависимости. Кроме внедрения через параметры функции, выделяют следующие способы:</p>
8
<ul><li>Через конструктор</li>
8
<ul><li>Через конструктор</li>
9
<li>Через сеттер</li>
9
<li>Через сеттер</li>
10
</ul><p>Кроме, собственно, полиморфизма, многим компонентам часто нужны готовые объекты, представляющие различные подсистемы программы. К таким компонентам могут относиться соединения с базой данных, доступы к кешам, любые компоненты с состоянием. Единственный способ получать к ним доступ без внедрения зависимостей - это использование глобальных переменных.</p>
10
</ul><p>Кроме, собственно, полиморфизма, многим компонентам часто нужны готовые объекты, представляющие различные подсистемы программы. К таким компонентам могут относиться соединения с базой данных, доступы к кешам, любые компоненты с состоянием. Единственный способ получать к ним доступ без внедрения зависимостей - это использование глобальных переменных.</p>
11
<p>Многие действительно так и делают, более того, экосистемы некоторых языков подталкивают к таким подходам. Например, в Ruby очень часто объекты делаются глобальными переменными.</p>
11
<p>Многие действительно так и делают, более того, экосистемы некоторых языков подталкивают к таким подходам. Например, в Ruby очень часто объекты делаются глобальными переменными.</p>
12
<p>Из этой ситуации есть несколько хорошо изученных выходов.</p>
12
<p>Из этой ситуации есть несколько хорошо изученных выходов.</p>
13
<h2>Service Locator</h2>
13
<h2>Service Locator</h2>
14
<p>Сервис-локатор (service locator) - это чуть более продвинутая альтернатива глобальным переменным. Этот паттерн подразумевает наличие одного глобального объекта, который и является сервис-локатором. В начале программы он инициализируется всеми нужными сервисами. В процессе жизни программы каждый компонент сам запрашивает у локатора нужные зависимости. Честно скажем, что этот подход так себе, но за неимением лучшего, может стать неплохим подспорьем.</p>
14
<p>Сервис-локатор (service locator) - это чуть более продвинутая альтернатива глобальным переменным. Этот паттерн подразумевает наличие одного глобального объекта, который и является сервис-локатором. В начале программы он инициализируется всеми нужными сервисами. В процессе жизни программы каждый компонент сам запрашивает у локатора нужные зависимости. Честно скажем, что этот подход так себе, но за неимением лучшего, может стать неплохим подспорьем.</p>
15
<h2>DI Container</h2>
15
<h2>DI Container</h2>
16
<p>Самый продвинутый вариант называется<em>Dependency Injection Container</em>. При таком подходе контейнер становится центральной частью системы. С одной стороны он предоставляет интерфейс для описания всех сервисов и их зависимостей, с другой стороны сам занимается созданием графа объектов, попутно внедряя зависимости в те места, где они нужны. Такой подход особенно распространён в таких языках как Java или C#. Возможно, вы даже слышали такое название, как Spring Framework.</p>
16
<p>Самый продвинутый вариант называется<em>Dependency Injection Container</em>. При таком подходе контейнер становится центральной частью системы. С одной стороны он предоставляет интерфейс для описания всех сервисов и их зависимостей, с другой стороны сам занимается созданием графа объектов, попутно внедряя зависимости в те места, где они нужны. Такой подход особенно распространён в таких языках как Java или C#. Возможно, вы даже слышали такое название, как Spring Framework.</p>
17
<p>Ниже представлен один из вариантов того, как могли бы выглядеть части системы, использующей контейнер:</p>
17
<p>Ниже представлен один из вариантов того, как могли бы выглядеть части системы, использующей контейнер:</p>
18
<p>Как видите, класс не запрашивает никаких зависимостей сам, они внедряются через конструктор какой-то внешней системой.</p>
18
<p>Как видите, класс не запрашивает никаких зависимостей сам, они внедряются через конструктор какой-то внешней системой.</p>
19
<h2>bottlejs</h2>
19
<h2>bottlejs</h2>
20
<p>bottlejs - это библиотека, которая позиционирует себя как<em>DI Micro Container</em>. В отличие от своих старших собратьев, она очень простая. С ее помощью достаточно легко собирать зависимости:</p>
20
<p>bottlejs - это библиотека, которая позиционирует себя как<em>DI Micro Container</em>. В отличие от своих старших собратьев, она очень простая. С ее помощью достаточно легко собирать зависимости:</p>
21
<p>За сценой Bottlejs выполняет создание объектов и инъекцию нужных зависимостей в соответствии с конфигурацией. Пример выше работает так:</p>
21
<p>За сценой Bottlejs выполняет создание объектов и инъекцию нужных зависимостей в соответствии с конфигурацией. Пример выше работает так:</p>
22
<p>Такой способ работы подходит в простых случаях, когда зависимости это объекты без конфигурации. В более сложных случаях понадобится метод factory():</p>
22
<p>Такой способ работы подходит в простых случаях, когда зависимости это объекты без конфигурации. В более сложных случаях понадобится метод factory():</p>
23
<p>Количество возможных сервисов и способов их сбора никак не ограничено. Bottlejs универсальный инструмент. Он может собирать все что угодно, любым нужным способом.</p>
23
<p>Количество возможных сервисов и способов их сбора никак не ограничено. Bottlejs универсальный инструмент. Он может собирать все что угодно, любым нужным способом.</p>