## @e22m4u/js-service *[English](README.md) | Русский* Реализация паттерна «Сервис-локатор» для JavaScript. ## Установка ```bash npm install @e22m4u/js-service ``` Модуль поддерживает ESM и CommonJS стандарты. *ESM* ```js import {Service} from '@e22m4u/js-service'; ``` *CommonJS* ```js const {Service} = require('@e22m4u/js-service'); ``` ## Назначение Модуль предлагает классы `ServiceContainer` и `Service`, которые можно использовать как по отдельности, так и вместе. - `ServiceContainer` - классическая версия сервис-локатора - `Service` - скрывает создание контейнера и его распространение Класс `Service` удобен, когда приложение имеет единственную точку входа, которая создается оператором `new`. Например, если такой точкой является класс `Application`, то мы могли бы унаследовать его от класса `Service`, и обращаться к другим сервисам методом `getService` не заботясь о создании и хранении их экземпляров. Кроме того, если другие сервисы так же наследуют от класса `Service`, то они могут обращаться друг к другу используя тот же метод `getService`, как если бы мы передавали сервис-контейнер между ними. ## `ServiceContainer` class Методы: - `get(ctor, ...args)` получить существующий или новый экземпляр - `has(ctor)` проверка существования конструктора в контейнере - `add(ctor, ...args)` добавить конструктор в контейнер - `use(ctor, ...args)` добавить конструктор и создать экземпляр - `set(ctor, service)` добавить конструктор и его экземпляр ### get Метод `get` класса `ServiceContainer` создает экземпляр полученного конструктора и сохраняет его для последующих обращений по принципу "одиночки". Пример: ```js import {ServiceContainer} from '@e22m4u/js-service'; // создание контейнера const container = new ServiceContainer(); // в качестве сервиса используем класс Date const myDate1 = container.get(Date); // создание экземпляра const myDate2 = container.get(Date); // возврат существующего console.log(myDate1); // Tue Sep 12 2023 19:50:16 console.log(myDate2); // Tue Sep 12 2023 19:50:16 console.log(myDate1 === myDate2); // true ``` Метод `get` может принимать аргументы конструктора. При этом, если контейнер уже имеет экземпляр данного конструктора, то он будет пересоздан с новыми аргументами. Пример: ```js const myDate1 = container.get(Date, '2025-01-01'); // создание экземпляра const myDate2 = container.get(Date); // возврат существующего const myDate3 = container.get(Date, '2025-05-05'); // пересоздание console.log(myDate1); // Wed Jan 01 2025 03:00:00 console.log(myDate2); // Wed Jan 01 2025 03:00:00 console.log(myDate3); // Sun May 05 2030 03:00:00 ``` ### Наследование Конструктор `ServiceContainer` первым параметром принимает родительский контейнер, который используется как альтернативный, если конструктор запрашиваемого экземпляра (сервиса) не зарегистрирован в текущем. ```js class MyService {} // создание контейнера и регистрация // в нем сервиса MyService const parentContainer = new ServiceContainer(); parentContainer.add(MyService); // использование предыдущего контейнера в качестве родителя, // и проверка доступности сервиса MyService const childContainer = new ServiceContainer(parentContainer); const hasService = childContainer.has(MyService); console.log(hasService); // true ``` ## `Service` class Методы: - `getService(ctor, ...args)` получить существующий или новый экземпляр - `hasService(ctor)` проверка существования конструктора в контейнере - `addService(ctor, ...args)` добавить конструктор в контейнер - `useService(ctor, ...args)` добавить конструктор и создать экземпляр - `setService(ctor, service)` добавить конструктор и его экземпляр Сервисом может являться совершенно любой класс. Однако, если это наследник класса `Service`, то такой сервис позволяет инкапсулировать создание сервис-контейнера, его хранение и передачу другим сервисам. Пример: ```js import {Service} from '@e22m4u/js-service'; // сервис Foo class Foo extends Service { method() { // доступ к сервису Bar const bar = this.getService(Bar); // ... } } // сервис Bar class Bar extends Service { method() { // доступ к сервису Foo const foo = this.getService(Foo); // ... } } // сервис App (точка входа) class App extends Service { method() { // доступ к сервисам Foo и Bar const foo = this.getService(Foo); const bar = this.getService(Bar); // ... } } const app = new App(); ``` В примере выше мы не заботились о создании сервис-контейнера и его передачу между сервисами, так как эта логика инкапсулирована в классе `Service` и его методе `getService` ### getService Метод `getService` обеспечивает существование единственного экземпляра запрашиваемого сервиса, а не создает каждый раз новый. Тем не менее при передаче дополнительных аргументов, сервис будет пересоздан с передачей этих аргументов конструктору. Пример: ```js const foo1 = this.getService(Foo, 'arg'); // создание экземпляра const foo2 = this.getService(Foo); // возврат существующего console.log(foo1 === foo2); // true const foo3 = this.getService(Foo, 'arg'); // пересоздание экземпляра const foo4 = this.getService(Foo); // возврат уже пересозданного console.log(foo3 === foo4); // true ``` ## Тесты ```bash npm run test ``` ## Лицензия MIT