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

Почему автоматическая регистрация зависимостей — зло

Время на прочтение 3 мин
Количество просмотров 5.5K
Всего голосов 24: ↑19 и ↓5 +14
Комментарии 91

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

НЛО прилетело и опубликовало эту надпись здесь
Так это же проблема сред разработки.

Да это так, но на сегодняшний день мне не известны среды в полной мере подержали тот инструментарий который доступен при явном объявление зависимостей.


Я думаю, при таком повороте событий отследить неконтролируемый размер проекта можно не только по мануально объявленным зависимостям.

Поделитесь своим опытом как вы контролируете рост сложности в проекте, мы ищем все возможности для сдерживания.


Странно обвинять инструмент в самой его сути. Он же именно это и должен делать, если кто-то неправильно гвозди забивает, то это не молоток виноват.

Я бы не стал называть это обвинением. Есть опасные инструменты, а есть менее опасные, просто нужно знать о возможных последствиях. Для нового разработчика на проекте нет ни одной подсказки о том что какая-то папка в проекте отличается от других и если в нее положить или убрать класс то поведение системы изменится. Он может об этом никогда и не узнать, т.к. баг упадет соседней команде через неделю после деплоя.

Так это же проблема сред разработки.

Если бы это было стандартной фичей языка, то да. Но обычно это вагон и маленькая тележка библиотек разной степени рефлексивности например.

Что хорошего в автоматической регистрации зависимостей?

То, что она позволяет расширять систему после сборки. Кастомизация, плагины, вот это вот всё.

… в каком-то смысле, это даже можно расширить: она удобна тогда, когда уже есть конвенция, по которой сущности попадают в систему (например, автообнаружение контроллеров в asp.net MVC/WebAPI), и нужно просто добавить к этим сущностям DI.

Только в 99% проектах нет никаких плагинов, а есть просто огромный InfrastructureExtensions.RegisterServices в тыщу строк, где просто 1:1 связываются интерфейсы с реализациями.

Ничего не знаю про 99% проектов, говорю про свой опыт, и где лично я использую автоматическую регистрацию.

Я ж не спорю, я говорю про свой опыт)

Просто ваш опыт — он не про автоматическую регистрацию, потому что "InfrastructureExtensions.RegisterServices в тыщу строк, где просто 1:1 связываются интерфейсы с реализациями" — это явно не автоматическая регистрация.

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

Согласшение об именах контроллеров ещё куда ни шло (но и тут про зависимости по сути ни слова)

Ну вот есть у вас контроллеры, они подхватываются системой автоматически. Мы хотим, чтобы контроллеры получали зависимости, как и все другие сервисы.

Я не спорю, зависимости подставлять это хорошо. Мне не нравятся текущие реализации.


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


Но почему-то такое пока не делают.

Так как в отличие от плагинных систем, в вебе мы всегда знаем что на что маппим

Вы, я так понимаю, не видели плагинных систем в вебе? У меня вот ровно такая.


почему-то такое пока не делают.

Может быть потому, что не хочется делать несколько разных DI-фреймворков для разного использования? Отдельный для веба, отдельный для десктопов, отдельный еще для чего-нибудь?

Вы, я так понимаю, не видели плагинных систем в вебе? У меня вот ровно такая.

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

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

"Нет" — не видели? Или что?

Не видел

Ну вот а я над такой работаю. И там без обнаружения в рантайме — никак.

Ну так а можно подробнее? Что у вас за система, что вам прям обязательно в рантайме сборки нужно подключать? Кроме условного 'eval' того что от пользователя прилетает у меня даже как-то вариантов с ходу в голоу не приходит.

Что у вас за система, что вам прям обязательно в рантайме сборки нужно подключать?

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


При этом доступа к исходному коду у него нет, а в случае, когда он ставит сторонние пакеты, и разработческого окружения у него тоже нет.

Ну в таком случае да, с диаем наверное круто.

В таком случае круто с автообнаружением реализаций. А с DI "круто" даже в случае без плагинов, а просто при определенной сложности системы (ну и определенном ее дизайне, конечно).

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

Зависит от того как это реализовано. Если у вас например программа после старта смотрит какие библиотеки лежат в папочке с плагинами и потом регистрирует найденные вещи, то как вы это ещё предлагаете делать?

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

Давайте для простоты допустим что у вас плагины не зависят друг от друга вообще и вам нужно зарегистрировать только сами плагины в хосте. Но плагины пишутся уже после того как хост был релизнут и всё что от них требуется это имплементация определённого интерфейса.

Давайте, согласен. И как может получиться так что эта ситуация приводит к тому что плагин не удалось создать?


Могу представить только ситуация когда в плагин передают DI контейнер из которого он произвольный тип пытается вытащить.

Теперь я не совсем понимаю что вы имеете ввиду. Ваш хост должен взять и зарегистровать рэндомное количество плагинов. Он знает только что плагины имплементируют определённые интерфейсы.


Как вы их собираетесь у себя регистрировать в хосте?

Я бы делал как-то так.


Для плагинной системы у меня в хосте была бы фабрика которая по имени папки ищет там все dll и в них все классы которые имплементируют интерфейс и при этом имеют конструктор который фабрика умеет вызывать передавая туда необходимые зависимости. Все остальные классы она(фабрика) просто игнорирует.


При это фактори будет уже создана со всеми необходимыми инстансами для того чтобы вызывать классы.

Эта "фактори" — это, внезапно, DI-контейнер как он есть. Вот прямо вот один в один. Только специализированный.

И как ваша фактори должна распознавать конструкторы и вызывать их? Через рефлексии?


И чем это вариант вообще лучше? Ну то есть какие проблемы он позволяет избежать по сравнению с автоматической регистрацией в DI?

Через рефлексию, точно так же как и Simple Injector и многие другие контейнеры. Да и другого способа инстанцирование класса из dll мне не известно.


Позволяет в хосте строго опистаь зависимости которые должны быть инициализированны на момент компиляции хоста и гарантировать что какие бы dll не лежали в папке хост не упадет при выполнение и все плагины соответствующие требованию будут инициализированы.


Единственно что мне не нравится в этом подходе(в вашем это проблема так же есть) это то что интерфейс не позволяет явно описать требования к плагину. Думаю что можно это сделать чреез описания интерфейса фабрики, но не хочется изобретать велосипед в коментариях, думаю что уже где-то есть готовое решение.

гарантировать что [...] все необходимые плагины будут инициализированы.

Вот ровно это вы и не гарантируете. Потому что если плагин запрашивает неподдерживаемую зависимость, вы его просто пропустите, не пытаясь инициализировать.

Не точно так же. У вас будет больше рефлексии и вам придётся самому заботиться о том чтобы она работала правильно. Да и вообще вам надо будет самому писать кучу дополнительных вещей, которык при автоматической регистрации вы получаете из коробки.


И я не вижу каким образом это позволяет что-то "строго описать". У вас же всё через рефлексии.


А проверить всё ли у вас нужное инициализииовалось или нет, элементарно можно и с автоматической регистрацией.

… да прямо скажем, там автоматическая регистрация и описана. Просто самописная.

Конструктор фабрики плагина он в хосте, зависимосте в нем явно описаны.


А проверить всё ли у вас нужное инициализииовалось или нет, элементарно можно и с автоматической регистрацией.

На этапе компиляции?

Допустим у вас есть интерфейс ILogger и какие-то вам неизвестные рэндомные имплементации этого интерфейса в плагинах. Что вы собираетесь прописывать в хосте в вашей фабрике?


Как вы на этапе компиляции собираетесь проверять работу рефлексий с рэндомными библиотеками? А если библиотеку банально забыли положить в нужную папку?

У меня создается ощущение, что вы не очень себе представляете, как обычно используется DI-контейнер для поддержки плагинов. Если вкратце, то вот так:


//composition root
containerBuilder.RegisterType<NotificationEngine>();
containerBuilder.RegisterAllImplementationsOf<INotificationSender>(); //отправители - это плагины

//plugin host
class NotificationEngine
{
  public NotificationEngine(INotificationSender[] senders)
    => _senders = senders;
  public void SendAsync(Notification notification)
    => _senders.ForEach(sender => sender.Send(notification));
}

Если я верно понял то тут еще где-то должен быть код чтения dll из папки, так?

Если я верно понял то тут еще где-то должен быть код чтения dll из папки, так?

Он внутри RegisterAllImplementationsOf.

Тогда весь серкет в том как работает функция RegisterAllImplementationsOf. Она может гарантировать какие классы из dll с какими конструкторами она берет. Если она берет произвольный класс с произвольным конструктором и ищет под него все ранее зарегестрированный инстанс и в случае не обнаружения такового падает то это не очень хороший вариант.


А может быть как я описал выше, что RegisterAllImplementationsOf инстанцирует только те плагины чьи конструкторы он считает подходящим.


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

Тогда весь серкет в том как работает функция RegisterAllImplementationsOf.

Она достает из заданного набора сборок все типы, которые реализуют заданный интерфейс.


ищет под него все ранее зарегестрированный инстанс и в случае не обнаружения такового падает то это не очень хороший вариант.

Что в нем плохого? Если мы хотим, чтобы некорректный плагин не ронял нам систему, мы просто оборачиваем его активацию в try-catch, где логируем эксепшн. Но чаще мы такого не хотим, поэтому падение — это хорошо.


А может быть как я описал выше, что RegisterAllImplementationsOf инстанцирует только те плагины чьи конструкторы он считает подходящим.

… и как автору плагина разобраться, почему его плагин не грузится?


Я бы рекомендовал разделять плагины по типам опираясь на требуемы им зависимости

Это куча совершенно избыточного кода, польза от которого неочевидна. Впрочем, если вам так хочется, то это дописывается через расширение для DI-контейнера, которое дополнительно фильтрует найденные регистрации.

… и как автору плагина разобраться, почему его плагин не грузится?

В вашем варианте это проблема так же акутальна.


Это куча совершенно избыточного кода

Это попытка в коментариях изобрести подход при котором на момент компиляции в хосте инстанцированы все необходимые классы для плагина.


Что в нем плохого?

То что от наличия в папке файла хост ведет себя не предсказуемо. Можно проглатить эксепшен и это будет ближе ко второму варианту с проверкой конструктора.

В вашем варианте это проблема так же акутальна.

О нет, в моем варианте эта проблема не актуальна, потому что ошибка будет либо в логе, либо, что вероятнее, в дампе падения.


Это попытка в коментариях изобрести подход при котором на момент компиляции в хосте инстанцированы все необходимые классы для плагина.

А зачем его изобретать, если современные DI-контейнеры (в частности, Autofac) это прекрасно умеют?


Только это все равно останется автоматической регистрацией зависимостей, которую вы называете в вашей статье злом.

О нет, в моем варианте эта проблема не актуальна, потому что ошибка будет либо в логе, либо, что вероятнее, в дампе падения.

Ничего не мешает фабрике выводить такие ошибки если требуется.


А зачем его изобретать, если современные DI-контейнеры (в частности, Autofac) это прекрасно умеют?

Раскажите как они могут гарантировать на момент компиляции хоста что все необходимые зависимости инстанцированы.

Раскажите как они могут гарантировать на момент компиляции хоста что все необходимые зависимости инстанцированы.

void ConfigureContainer(ContainerBuilder containerBuilder, Dep1 d1, Dep2, d2, Dep3 d3)
{
  containerBuilder.RegisterInstance(d1);
}

(только вам еще нужен язык с explicit nullability)

Так а что тут осталось от автоматической регистрации? Вы же явно описали имплиментации даже без интерфейсов.

Так а что тут осталось от автоматической регистрации? Вы же явно описали имплиментации даже без интерфейсов.

А то, что вы смотрите на регистрацию зависимостей для плагинов, а не самих плагинов. Плагины дальше точно так же регистрируются через RegisterAssemblyTypes с фильтром по конструктору.

Я постарался донести свой взгляд, надеюсь это было для вас полезно.

А я не говорил про систему автоматического разрешения зависимостей (я даже не знаю, что это такое), я говорил про систему автоматической регистрации зависимостей.


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

Раз не требуется значит мне показалось )

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

"Не требуется", да. Но так намного удобнее.


(ну то есть не точно так, а так, как сделано в нормальных DI, но это детали имплементации)

Я по сути и обсуждаю с вами то, что за это удобство приходиться платить стабильностью сиситемы.

Неа, не приходится. При адекватно спроектированной системе для покрытия этого тестами нужно минимальное количество тестов (в идеальном случае — один смок-тест, потому что достаточно собрать composition root и запустить точку входа).

тесты это дорого

Они все равно нужны. А в данном случае вам не нужен дополнительный тест, достаточно существующих.


не явно для разработчика.

Если у вас тесты — это неявно для разработчика, то я могу вам только посочувствовать.

Я согласен с тем что можно покрыть так систему тестами что проверка типов и не потребуется, если вам так удобнее фиксировать требования и писать самодокументируемый код, то вам действительно автоматическая регистрация зависимостей может подойти больше чем проверка на этапе компиляции.

вам действительно автоматическая регистрация зависимостей может подойти больше чем проверка на этапе компиляции.

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

Я согласен с этим, поэтому я бы настоятельно не рекомендовал добавлять ЗАВИСИМОСТИ в рантайме. Плагины это не зависимости если от них не зависят другие плагины.

Плагины это не зависимости если от них не зависят другие плагины.

Нет, плагины — это зависимости, потому что от них зависят какие-то компоненты в системе, которые ими пользуются.

В этом случае это зависимость и проконтролировать на этапе компиляции это не возможно, вы правы. Действительно именно в этом случае такая автоматическая система действительно не добавляет проблем.

Иногда система очень большая и модульная и все зависимости смысла ставить нет. И тут, я б сказал, неявная регистрация зависимостей(которая может быть автоматической) очень помогает.

А и правда, что хорошего? Предлагаю в комментариях подискутировать, почему же этот метод набрал популярность. Наверняка же у него есть поклонники среди читателей.

Модулярность и гибкость.

Вы можете при таком подходе просто дать людям ядро и пусть они сами пишут «отсутствующую» имплементацию там где её нет. Причём именно так как это нужно им самим.
Вы можете поменять функциональность программы просто поменяв только библиотеки с имплементациями определённых интерфейсов.
Вы можете вообще запихать регистрацию зависимостей нужных для работы модуля в сам модуль.

И так далее и тому подобное.

В целом, соглашусь с посылом статьи. Не понимаю любовь кучи разработчиков в 2020 году получать в лицо ошибки в рантайме вместо ошибки компиляции. Ошибка что параметра не хватает? Ну нафиг, лучше эксепшн "тип не зарегистрирован". Добавил новое поле в объект, нужна ошибка "инициализатор не полон"? Нее, лучше я от автомаппера InvalidOperation схвачу в лицо. С таким подходом непонятно, почему бы в условный питон не пойти писать: там то же самое, зато есть вся гибкость динамики. Раз уж тайпчекер нам все равно ни к чему и эксепшны в рантайме предпочтительнее.


При этом в не-ООП языках такой фигни нет. В расте зависимости прокидываются явно и я бы не сказал что там какой-то ад и ничего неудобно. Точно так же можно в одном месте зависимости менять с одной реализации на другую и т.п. Доходит до смешного, когда в консольном приложении на пару тысяч строк делается di.RegisterService<Foo>(); var foo = di.GetService<Foo>(); прямо в static void Main. Как по мне просто эталонный случай карго культа.


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

Не понимаю любовь кучи разработчиков в 2020 году получать в лицо ошибки в рантайме вместо ошибки компиляции.

«Не понимаю этой любви разработчиков с к базам данных. Получить ошибку соединения ....»

Вы это не понимаете потому что у вас use case'ов таких может быть нет. Но если у вас их нет, то это не значит что ни у кого их нет.
«Не понимаю этой любви разработчиков с к базам данных. Получить ошибку соединения ....»

Не относящаяся к делу аналогия


Вы это не понимаете потому что у вас use case'ов таких может быть нет. Но если у вас их нет, то это не значит что ни у кого их нет.

Каких юзкейсов у меня нет? Зависимостей? Уверяю, что есть.

Не относящаяся к делу аналогия

Почему же? Зачем вам нужны базы данных с их проблемами если всю информацию можно хранить в файлах?

Каких юзкейсов у меня нет? Зависимостей? Уверяю, что есть.

У вас есть use case когда например надо поменять/расширить работу системы без возможности перекомпилировать всю систему? У вас есть use case когда например часть имплементации предоставляет сам клиент через плагины?

И как вы их решаете?

Нет, я как и большинство других разработчиков занят в области веб разработки, там нет необходимости в каких-либо плагинах. И 10 из 10 сервисов продолжает использовать диай с автоматической регистрацией.

там нет необходимости в каких-либо плагинах.

Поправка: у вас нет.


И 10 из 10 сервисов продолжает использовать диай с автоматической регистрацией.

Поправка: десять из десяти виденных вами сервисов. Я, скажем, не использую автоматическую регистрацию (вне зависимости от того, веб это или не веб), если для этого нет конкретной потребности.

Ну во первых насчёт «большинство в вебе» я как бы не особо то и уверен. Есть какая-то статистика по этому вопросу?

Bо вторых и в вебе вполне себе существуют и плагины и необходимость адаптаций работающей системы в том или ином виде. То есть я вполне себе видел сервисы у которых библиотеки меняли «на живую» без остановки самого сервиса.

Ну и в третьих даже если в вебе это и не надо, то это не значит что это нигде не надо. А ни в самой статье, ни в ваших комментариях вроде бы не указано что речь идёт исключительно о вебе…
Ну во первых насчёт «большинство в вебе» я как бы не особо то и уверен. Есть какая-то статистика по этому вопросу?

Моя личная из нескольких сотен сервисов в 5 разных компаниях что я видел.


Bо вторых и в вебе вполне себе существуют и плагины и необходимость адаптаций работающей системы в том или ином виде. То есть я вполне себе видел сервисы у которых библиотеки меняли «на живую» без остановки самого сервиса.

В компаниях где я видел накатывание "на живую" заключалось в канареечном релизе и переноса нагрузки со старого инстаса на новый.


Ну и в третьих даже если в вебе это и не надо, то это не значит что это нигде не надо. А ни в самой статье, ни в ваших комментариях вроде бы не указано что речь идёт исключительно о вебе…

Так а кроме веба разве что-то ещё есть. Тем более с плагинами. Я знаю, на дотнете есть фреймворк MEF для плагинов. Вакансий где требовался опыт его использованием видел примерно ноль.

То есть вы делаете такие глобальные и далеко идущие выводы только на основании своего относительно небольшого опыта? Интересный подход....

Ну я пока не встречал сверхлюдей, которые умеют делать вывод на основании того что они не знают или не видели. У всех свой опыт, правда у кого-то это "читал статью на хабре". А так — можно посмотреть на ХХ на срез используемых технологий и сделать вывод.

Хм. А что например мешает перед тем как делать выводы исключительно на основании личного опыта, вот просто взять и спросить других людей? Или просто ещё как то проинформироваться? Особенно если знаешь что собственный опыт не особо обширен?

Особенно если знаешь что собственный опыт не особо обширен?

Я считаю, что выборка достаточная чтобы делать какие-то выводы. Сотни компонентов, чуть меньше сотни собесов, пять совершено разных компаний — да, я считаю что это определенный опыт и значимый срез индустрии.


Вот с чего вы взяли что у вас — тот самый "значимый срез индустрии"? Если вы так не считаете, тогда к чему эта ветка комментариев?

Я считаю, что выборка достаточная чтобы делать какие-то выводы. Сотни компонентов, чуть меньше сотни собесов, пять совершено разных компаний — да, я считаю что это определенный опыт и значимый срез индустрии.

И при этом исключительно веб и достаточно похожие задачи. Так себе выборка.

Вот с чего вы взяли что у вас — тот самый «значимый срез индустрии»? Если вы так не считаете, тогда к чему эта ветка комментариев?

А я и не делаю выводы исключительно по своему личному опыту.
И при этом исключительно веб и достаточно похожие задачи. Так себе выборка.

Так веб это и есть 90%+ разработки (если не считать мобилки).


А я и не делаю выводы исключительно по своему личному опыту.

А по чьему ещё? То что вы от знакомых знаете или в статьях это ведь тоже получается личный опыт.

Так веб это и есть 90%+ разработки (если не считать мобилки).

Ну да, по вашему личному опыту :)

А по чьему ещё? То что вы от знакомых знаете или в статьях это ведь тоже получается личный опыт.

Нет. Личный опыт так называется потому что получается лично :)

Расскажите про отсутствие необходимости тем, кто работает с CMS и вообще самостоятельно разворачиваемым софтом. Не все работают по SaaS и ко.

Самый популярный вебсофт — WordPress вроде как. И там всё основано на устанавливаемых пользователем плагинах.

«Не понимаю этой любви разработчиков с к базам данных. Получить ошибку соединения ....»

Пишите тогда уж точнее, что ли: «Не понимаю этой любви рахработчиков к выделенным серверам баз данных». Потому как это — про другой уровень абстракции и другой уровень разделения обязанностей: между разными единицами аппаратуры по сети. То есть, в вашей аналогии на самом деле оказывается, что речь идет примерно о той же разнице, как разница между модульной и микросервисной архитектурой.
Потому как вообще-то базы данных могут быть и встренные (например Embedded SQL) — исторически на мейнфреймах, где они появились, примерно так и было, и там не было места ошибкам сетевого соединения.

Исходя из опыта мобильной разработки, когда код довольно "многослоен" то в целом это удобно (в паре с кодогенерацией), так как достаточно расставить аннотации типа @Injectable, сделать иньекцию в одном месте (в условном view из MVP), и у тебя уже готовое дерево зависимостей, вместо того чтобы руками передавать в конструктор presenter'a многослойный пирог зависимостей. По сути это только погоня за уменьшением бойлерплейта, переложить всё на плечи генератора кода, и реально DI имееет смысл только если есть некое разделение кода (dev, production и test) или плагино-подобная система.

У меня просто не раз и не два возникала ситуация "забыли зарегистрировать зависимость", причем один раз был такой момент, что интерфейс в одной сборке, реализация в другой, и компонент в который это инжектилось не мог ссылаться на сборку с реализацией физически (там несовместимости на уровне библиотек были). Узнали об этом уже на регрессе когда этот редкий компонент начали проверять, и оказалось что его аффектит.


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

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

Как вариант, такую проблему мог бы решить ЯП в котором DI контейнеры были бы встроенной фичей языка и валидировались на этапе компиляции.
Альтернативным способом видится написание кодогенератора, который на основе метаданных будет создавать код, инстанцирующий объекты на этапе компиляции.
Опционально да. Без возможности отключить однозначно нет.

Да и как вы это собираетесь проверять? Вот написал я библиотеку и у меня конструкторы имеют определённые интерфейсы в параметрах. И дальше что? Вызывать то конструкторы будет кто-то другой и не факт что у него вообще есть DI.

Всё придумано до нас — HList и в путь. тогда регистрация выглядит как


di = As<Foo, Bar> :: As<Baz,Booz> :: HNil

А дальше как привыкли


serviceFoo = di.GetService<Foo>();
serviceBoo = di.GetService<Boo>(); // ошибка компиляции, не зарегистрирован
Если окинуть всю картину общим взглядом, то становится понятным, откуда что и для чего появляется.
Ну, интерфейсы в качестве параметров конструкторов — это, понятно, буква «D» в cлове SOLID. В это слово сейчас положено верить, и на это вроде бы даже есть основания, которые я сейчас совсем не хочу рассматривать по существу. Примем на веру: SOLID рулит.
Отдельная подсистема, которая порождает все классы, реализующие интерфейсы — это, конечно, Abstract Factory pattern. Следовать ли ему? Если реализации разных интерфейсов внутри могут быть довольно сильно связаны (к примеру — использовать общее хранилище), и если есть большое подозрение, что реализации будут меняться(а в больших проектах такое надо подозревать IMHO всегда), то Abstract Factory pattern — это практически неизбежность. Вот если реализации разных интерфейсов друг от друга зависят слабо — тогда можно попробовать обойтись и без этой подсистемы.
Ну, и последний вопрос — зачем подсистему создания реализаций интерфейсов делать сложной и таинственной, наполненной нетривиальным внутренним сожержимым. И ответ здесь — соблазн декларативности. Каждому человеку (и программисту тоже) хочется просто указать компьютеру, что надо делать — и пусть он решит, как именно сделать. Оборотная сторона тут тоже есть: компьютер сделает ровно то, что вы ему приказали, а как и почему он сделал это — придется выяснять, и это может быть дольше, чем искать ошибку в собственном коде. И да, автор прав: выясниться это может в самый неподходящий момент. Но не все эту оборотную сторону видят, особенно — теоретики от программирования из Computer Science: это же не им приходится отлавливать ошибки.
Как к этому относиться — зависит от конкретных условий. Если архитектуру определяете вы самостоятельно — можно попробовать подумать и оценить. Но для этого нужен опыт, и что делать, если опыта нет — не знаю. Навереное, в этом случае лучше все же следовать моде, делать как все делают. Если же вам поставлено условие типа «используем DI-контейнер X» то тут можно только соглаисться на это условие. Или — не согласиться, со всеми вытекающими.
Непонятно чем это таким Simple Injector провинился, контейнер как контейнер. Если он в чём-то и хорош — то точно не в автоматической регистрации зависимостей, хотя, конечно же, имеет такую возможность.

Ничем. Simple Injector в статье исключительно для прмера, мы его продолжаем использовать для .net MVC проектов, но создаем все зависимости в ручную.

Явное почти всегда лучше неявного. А автоматическое внедрение зависимостей еще и магией попахивает. От такого надо держаться подальше.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий