Как стать автором
Обновить

Комментарии 5

Как я понял, в вашей библиотеке именно клиент зависимости управляет тем, как она будет создана, с помощью вот этой сложной системы идентификаторов. Но мне кажется, что от DI скорее ждут того, чтобы клиент получал уже готовую зависимость. То, как ее можно создавать - фабрикой или конструктором, например - могла бы сообщать контейнеру сама зависимость. А синглтоном она будет или нет, кажется, должно решать само приложение, тот его код и конфигурация, которые собирают все модули воедино.

Более того, насколько вижу, у вас структура этих идентификаторов получается привязанной к пути к файлу с реализацией зависимости - а как при этом зависеть от интерфейса, а не конкретной реализации?

В итоге, ваши примеры кажутся полностью идентичными просто обычным импортам, без каких-либо преимуществ. Мы все так же просто указываем файл с реализацией зависимости и как ее создать - просто теперь почему-то в виде отдельного DSL.

Но мне кажется, что от DI скорее ждут того, чтобы клиент получал уже готовую зависимость. То, как ее можно создавать - фабрикой или конструктором, например - могла бы сообщать контейнеру сама зависимость.

Так и есть. В es6-модуле экспортом может быть что угодно, включая класс и фабричную функцию. Будет ли этот экспорт внедрён as-is или будет внедрён результат выполнения фабричной функции (конструктора класса) - зависит от контейнера. Который конфигурируется при помощи идентификаторов зависимости в месте внедрения зависимости (могла бы сообщать контейнеру сама зависимость). Так в одном случае один и тот же экспорт должен внедряться as-is, в другом - как синглтон, в третьем - как инстанс (можно даже в пределах одного конструктора):

export default class Service {
    constructor(
        {
            'Ns_Module.export': asIs,
            'Ns_Module.export$': asSingleton,
            'Ns_Module.export$$': asInstance,
        }
    ) {}
}

Тут можно говорить ещё об одном контуре IoC - когда конфигурация контейнера идёт не через сам контейнер, а через клиентов, которые он обслуживает.

Более того, насколько вижу, у вас структура этих идентификаторов получается привязанной к пути к файлу с реализацией зависимости - а как при этом зависеть от интерфейса, а не конкретной реализации?

Вы правы, идентификатор привязывается к пути в исходнику, если исходник существует. Но в моей реализации контейнера после этапа разбора идентификатора (например, Fl32_Auth_Back_Api_Mod_User) идёт этап пред-обработки, где Контейнер может заменить в структуре идентификатора любую из его частей или все сразу:

Если в настройках Контейнера на уровне соответствующего приложения прописать, что такой-то интерфейс должен заменяться такой-то имплементацией, то в таком-то приложении будет использоваться соответствующая имплементация вместо интерфейса.

В саму библиотеку этот функционал не входит. На её уровне реализованы только возможности подключения цепочек препроцессоров и постпроцессоров, а правила пред- и пост-обработки уже закладываются отдельно, в зависимости от приложения и его процессоров.

В итоге, ваши примеры кажутся полностью идентичными просто обычным импортам, без каких-либо преимуществ.

Без пред- и пост-обработки так бы и было. Но теперь связывание выполняется в runtime, а не в коде, и правила связывания могут динамически меняться в зависимости от приложения (подключенных к Контейнеру процессоров). Например, плагин аутентификации, работающий на своём уровне с интерфейсом Fl32_Auth_Back_Api_Mod_User, в одном приложении получит одну модель пользователя, а в другом - другую. В задачи плагина аутентификации входит связывание этой модели с контекстом HTTP-запроса и его не интересует, что там за модель. Как Стетхем в "Перевозчике" - "Никогда не открывать посылку". Плагин в одном месте запрашивает у приложения модель пользователя, а в другом - предоставляет приложению возможность эту модель забрать. И один и тот же плагин может работать без изменений в разных приложениях, если они придерживаются контракта (имплементируют интерфейс).

Зачем столько сложностей, если всё делается куда проще?

export class $my_app extends $my_base {
  
  do_something() {
    const user = this.$.$my_user.create()
    this.$.$acme_log_rise( 'User created with id=', user.id )
  }
  
}

Я зашел проверить есть ли ты тут)) Ты как всегда - хорош!

"Инверсия контроля на голом TypeScript без боли" vs. "Формат описания идентификатора зависимости в JS DI"

(TS != JS) && (DI != AmbientContext) 

Если вы этого не понимаете, то я не смогу вам ответить на вопрос "зачем" :(

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории