|
@@ -3,11 +3,11 @@
|
|
|
Реализация паттерна «Сервис-локатор» и принципа «Инверсия управления» (IoC)
|
|
Реализация паттерна «Сервис-локатор» и принципа «Инверсия управления» (IoC)
|
|
|
для JavaScript.
|
|
для JavaScript.
|
|
|
|
|
|
|
|
-## Оглавление
|
|
|
|
|
|
|
+## Содержание
|
|
|
|
|
|
|
|
- [Установка](#Установка)
|
|
- [Установка](#Установка)
|
|
|
- [Мотивация](#Мотивация)
|
|
- [Мотивация](#Мотивация)
|
|
|
-- [Содержание](#Содержание)
|
|
|
|
|
|
|
+- [Описание](#Описание)
|
|
|
- [Базовые примеры](#Базовые-примеры)
|
|
- [Базовые примеры](#Базовые-примеры)
|
|
|
- [ServiceContainer](#ServiceContainer)
|
|
- [ServiceContainer](#ServiceContainer)
|
|
|
- [Иерархия контейнеров](#Иерархия-контейнеров)
|
|
- [Иерархия контейнеров](#Иерархия-контейнеров)
|
|
@@ -50,7 +50,7 @@ const {Service} = require('@e22m4u/js-service');
|
|
|
`UserService`, который нуждается в логгере `Logger` и клиенте для доступа
|
|
`UserService`, который нуждается в логгере `Logger` и клиенте для доступа
|
|
|
к базе данных `DatabaseClient`.
|
|
к базе данных `DatabaseClient`.
|
|
|
|
|
|
|
|
-**Без библиотеки это может выглядеть так:**
|
|
|
|
|
|
|
+Без библиотеки это может выглядеть так:
|
|
|
|
|
|
|
|
```js
|
|
```js
|
|
|
// зависимости
|
|
// зависимости
|
|
@@ -94,22 +94,18 @@ const userService = new UserService(dbClient, logger);
|
|
|
userService.findUser(123);
|
|
userService.findUser(123);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**С какими проблемами мы сталкиваемся:**
|
|
|
|
|
|
|
+Недостатки ручного управления зависимостями:
|
|
|
|
|
|
|
|
-- **Жесткая связь и сложность**
|
|
|
|
|
|
|
+- Жесткая связь и сложность.
|
|
|
Точка входа в приложение превращается в "фабрику", которая знает,
|
|
Точка входа в приложение превращается в "фабрику", которая знает,
|
|
|
как создавать и связывать все компоненты. Если `DatabaseClient` тоже
|
|
как создавать и связывать все компоненты. Если `DatabaseClient` тоже
|
|
|
начнет зависеть от `Logger`, порядок создания усложнится.
|
|
начнет зависеть от `Logger`, порядок создания усложнится.
|
|
|
|
|
|
|
|
-- **Проблемы с тестированием**
|
|
|
|
|
|
|
+- Проблемы с тестированием.
|
|
|
Чтобы протестировать `UserService` в изоляции, нужно создать "моки"
|
|
Чтобы протестировать `UserService` в изоляции, нужно создать "моки"
|
|
|
для `Logger` и `DatabaseClient` и вручную передать их в конструктор.
|
|
для `Logger` и `DatabaseClient` и вручную передать их в конструктор.
|
|
|
Это громоздко и требует дополнительного кода в каждом тесте.
|
|
Это громоздко и требует дополнительного кода в каждом тесте.
|
|
|
|
|
|
|
|
-- **Неявные зависимости**
|
|
|
|
|
- Конструктор `UserService` требует определенные зависимости,
|
|
|
|
|
- и если их не передать, приложение упадет в рантайме.
|
|
|
|
|
-
|
|
|
|
|
#### Решение: Инверсия управления
|
|
#### Решение: Инверсия управления
|
|
|
|
|
|
|
|
Эта библиотека была создана для решения именно этих проблем. Она реализует
|
|
Эта библиотека была создана для решения именно этих проблем. Она реализует
|
|
@@ -117,7 +113,7 @@ userService.findUser(123);
|
|
|
и предоставлением зависимостей передается от компонента к специализированному
|
|
и предоставлением зависимостей передается от компонента к специализированному
|
|
|
контейнеру.
|
|
контейнеру.
|
|
|
|
|
|
|
|
-**С библиотекой код становится проще:**
|
|
|
|
|
|
|
+С библиотекой код становится проще:
|
|
|
|
|
|
|
|
```js
|
|
```js
|
|
|
import {Service} from '@e22m4u/js-service';
|
|
import {Service} from '@e22m4u/js-service';
|
|
@@ -155,22 +151,22 @@ const userService = new UserService();
|
|
|
userService.findUser(123);
|
|
userService.findUser(123);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**Преимущества этого подхода:**
|
|
|
|
|
|
|
+Преимущества этого подхода:
|
|
|
|
|
|
|
|
-- **Слабая связанность**
|
|
|
|
|
|
|
+- Слабая связанность.
|
|
|
`UserService` больше не знает, как создавать `Logger` или `DatabaseClient`.
|
|
`UserService` больше не знает, как создавать `Logger` или `DatabaseClient`.
|
|
|
Он просто заявляет о своей потребности в них, вызывая `this.getService()`.
|
|
Он просто заявляет о своей потребности в них, вызывая `this.getService()`.
|
|
|
|
|
|
|
|
-- **Простота и чистота кода**
|
|
|
|
|
|
|
+- Простота и чистота кода.
|
|
|
Точка входа в приложение становится тривиальной. Логика сборки зависимостей
|
|
Точка входа в приложение становится тривиальной. Логика сборки зависимостей
|
|
|
полностью инкапсулирована внутри контейнера, который неявно управляется
|
|
полностью инкапсулирована внутри контейнера, который неявно управляется
|
|
|
базовым классом `Service`
|
|
базовым классом `Service`
|
|
|
|
|
|
|
|
-- **Легкость тестирования**
|
|
|
|
|
|
|
+- Легкость тестирования.
|
|
|
Подменить зависимость стало невероятно просто. Метод `setService` позволяет
|
|
Подменить зависимость стало невероятно просто. Метод `setService` позволяет
|
|
|
"на лету" подставить мок-объект.
|
|
"на лету" подставить мок-объект.
|
|
|
|
|
|
|
|
-**Пример подмены зависимости (mocking):**
|
|
|
|
|
|
|
+Пример подмены зависимости (mocking):
|
|
|
|
|
|
|
|
```js
|
|
```js
|
|
|
// в файле теста
|
|
// в файле теста
|
|
@@ -191,7 +187,7 @@ userService.findUser(456);
|
|
|
может сосредоточиться на бизнес-логике, а не на том, как и в каком порядке
|
|
может сосредоточиться на бизнес-логике, а не на том, как и в каком порядке
|
|
|
создавать и связывать объекты.
|
|
создавать и связывать объекты.
|
|
|
|
|
|
|
|
-## Содержание
|
|
|
|
|
|
|
+## Описание
|
|
|
|
|
|
|
|
Модуль экспортирует два основных класса, `ServiceContainer` и `Service`,
|
|
Модуль экспортирует два основных класса, `ServiceContainer` и `Service`,
|
|
|
которые можно использовать как по отдельности, так и вместе для построения
|
|
которые можно использовать как по отдельности, так и вместе для построения
|