Re: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 09:44
Оценка: 2 (1) -1
Здравствуйте, zelenprog, Вы писали:


Z>А как это делается?

Z>По идее должна быть какая-то "центральная" точка, которая при инициализации создает конкретные объекты-реализации и передает их в Бизнес-логику.
Z>Но ведь невозможно же заранее "знать" какие объекты могут понадобиться в программе. Получается Бизнес-логика должна сама создавать тот объект, который ей понадобится.
Z>А как ей указать класс\тип объекта, который она должна использовать?
Z>Как правильно надо сделать?

То, что Вам нужно называется Dependency Injection Container. Дальше есть 3 варианта:

1. Часть реализаций можно там зарегистрировать сразу (те, что известны заранее), часть — во время выполнения программы. Как это сделать и возможно ли — зависит от используемого контейнера.

2.Не делать динамическую регистрацию, а использовать factory class, который будет статически лежать в контейнере и возвращать нужную реализацию, например, репозитория, выбирая её в рантайме.
Это и будет та самая "центральная точка"
Так можно с любым контейнером

3. Зарегистрировать в контейнере factory method. который будет возщвращать нужный интерфейс на информации, куда-то уже сохранённой приложением.
Это тоже будет та самая "центральная точка".
Re[6]: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 15:36
Оценка: 6 (1)
Здравствуйте, ·, Вы писали:

V>>·>Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.

V>>Вот сколько использовал контейнеры, никогда не инициализировал их через XML. КМК, это жуткое извращение.
·>Ну не важно, детали имплементации. Главное проблема — словарь, который ошибки компиляции превращает в отложенные ошибки исполнения.
Да, есть такой недостаток. Но на практике эти ошибки довольно редки и быстро локализуются.
Тут по сути возможны всего две ошибки: попытка использования незарегистрированного объекта и попытка использовать объект с меньшим временем жизни, чем использующий.
Обе они вылетают сразу в момент создания использующего объектаэ

V>>Жутким наследием они лично мне не кажутся, у них вполне есть своя ниша, где они удобны. Тут, главное, не увлекаться и не пихать их вообще вовсюда да ещё с интерфейсами для каждого класса.

V>>Хотя да, когда их используют индусы — иногда становится страшно, видел такое неоднократно.
·>Какая ниша-то? Делать проджект более солидным?
Те проекты, где нужно отделить логику связи объектов от логики создания объектов. Как правило, это удобно, например, в типовых web проектах на ASP.NET или в проектах с бизнес-компонентами, которые друг друга используют.
Тот пример, который Вы привели выше (императивная связь объектов) ведёт к дублированию кода и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.
Re[3]: Инициализация приложения - внедрение зависимостей в DDD
От: · Великобритания  
Дата: 10.11.23 11:21
Оценка: +1
Здравствуйте, zelenprog, Вы писали:


V>>То, что Вам нужно называется Dependency Injection Container. Дальше есть 3 варианта:

Z>Почитал про контейнеры...
Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.

Z>А что делать, если в "моей" платформе (среде разработки) нету контейнеров?

Просто пишешь отдельный код сборки (wiring), создающий объекты, передающий зависимости через конструктор:

Код типа
 var programm = new IndexPageController(new ProfileService(new ProfileRepository()));

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

Z>Можно ли написать свой класс типа "самопальный" контейнер?

Не нужно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 11:33
Оценка: +1
Здравствуйте, zelenprog, Вы писали:


Z>Почитал про контейнеры...


Z>А что делать, если в "моей" платформе (среде разработки) нету контейнеров?

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

Z>Тогда получается, что каждый класс программы должен иметь ссылку на этот объект "самопального" контейнера?

каждый, где нужно его содержимое.
Re[4]: Инициализация приложения - внедрение зависимостей в DDD
От: microuser  
Дата: 10.11.23 13:13
Оценка: +1
>>Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.
XML это только в богомерзкой жаве, в C# контейнеры изначально работали без конфигов и все прекрасно.

Z>>А что делать, если в "моей" платформе (среде разработки) нету контейнеров?

·>Просто пишешь отдельный код сборки (wiring), создающий объекты, передающий зависимости через конструктор:

·>Код типа

·>
·> var programm = new IndexPageController(new ProfileService(new ProfileRepository()));
·>

·>пишется, отлаживается и поддерживается гораздо проще, чем ровно то же самое, но через задни контейнер.
Ага, очень удобно потом при добавлении новой зависимости в 50 местах прописывать ее инициализацию, вот уж где "задний контейнер"
Re[8]: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 16:08
Оценка: +1
Здравствуйте, ·, Вы писали:

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

V>>Обе они вылетают сразу в момент создания использующего объектаэ
·>Верно. А в нормальном wiring коде такие ошибки просто подсвечиваются красным как ошибка компиляции ещё в процессе набора кода.
Это просто баланс интересов. Минимальное дублирование кода и отделение логики создания объектов ценой потенциальных ошибок в рантайме.

V>>Тот пример, который Вы привели выше (императивная связь объектов) ведёт к дублированию кода

·>Ты всё ещё не проникся. Wiring код это самый обыкновенный код, который пишется ровно так же как и весь остальной код. Скажем, дублирование можно избежать выносом одинаковых кусков в переиспользуемый метод или, например, оборачиванием в какой-нибудь цикл.
·>Если ты умеешь избегать дублирование кода в принципе, то это же умение точно так же работает и для wiring-кода.
Я как раз проникся. Как только мы в этом wiring коде отделяем логику создания объектов с целью его переиспользования от логики их связи, мы как раз и получаем самый обыкновенный DI контейнер, разве что статический.
С семи же потенциальными проблемами рантайма(забыли создать объект, который потом пытаемся связать).
Тут, правда, поможет новая фишка C# про nullable reference types, но только в не очень сложных случаях.

V>>и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.

·>Эээ... Фабрика/билдер?
Ну и напишете вручную тот же код, который уже есть в DI фреймворке. И в чём тогда плюсы?
Инициализация приложения - внедрение зависимостей в DDD
От: zelenprog  
Дата: 10.11.23 07:56
Оценка:
Здравствуйте!

Подскажите как осуществляется начальная инициализация ("сборка") всех зависимостей в приложении, построенном с помощью подхода DDD?

Программа конвертирует контрагентов из старой БД в новую.
Допустим я написал весь код в виде отдельных модулей:
— код Domain-модели, который делает сопоставление старых и новых контрагентов
— код Domain-модели, который делает маппинг (непосредственно конвертация) старого контрагента в нового и вызывает запись нового в базу с помощью Репозитория
— код для используемых Repository,
— код для Вьюшек
и т.д.

В целом все сделано и все работает.

Не сделан только один момент: сейчас везде в коде используются "конкретные" реализации, там где должны использоваться интерфейсы.
Например, в Бизнес-слое при записи Сущности вызывается конкретный Репозиторий, хотя ссылка на него хранится в виде интерфейса.
Теперь нужно вынести инициализацию всех этих интерфейсов куда-то так, чтобы можно было подменить реализации.

Это нужно сделать для того, чтобы применить эту программу для конвертации другого справочника из старой БД.
Например, для конвертации товаров. Суть обработки товаров такая же как и обработка контрагентов. Отличается только часть полей.
Поэтому суть программы тоже не поменяется и останется такой же. Вьюшки на 90% не поменяются, поменяются только часть колонок в отображаемых таблицах.
Значительно поменяется только реализация маппинга и репозиториев.

Программный код реализаций для обработки товаров уже написан.
Осталось только как-то сделать "инициализацию" всего приложения, то есть "пробросить" в Бизнес-логику другую реализацию.

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

А как это делается?
По идее должна быть какая-то "центральная" точка, которая при инициализации создает конкретные объекты-реализации и передает их в Бизнес-логику.
Но ведь невозможно же заранее "знать" какие объекты могут понадобиться в программе. Получается Бизнес-логика должна сама создавать тот объект, который ей понадобится.
А как ей указать класс\тип объекта, который она должна использовать?

В интернете нашел похожее обсуждение:

... создание программы со всеми зависимости будет выглядеть следующим образом:
var programm = new IndexPageController(new ProfileService(new ProfileRepository()));

Согласитесь — выглядит ужасно. Даже с учетом того что в программе всего две зависимости, это уже выглядит ужасно. Что уже говорить про программы в которых сотни и тысячи зависимостей.

https://habr.com/ru/articles/493430/

Но там используется незнакомая мне технология.

Как правильно надо сделать?
Отредактировано 10.11.2023 7:59 zelenprog . Предыдущая версия .
Re[2]: Инициализация приложения - внедрение зависимостей в DDD
От: zelenprog  
Дата: 10.11.23 11:11
Оценка:
V>То, что Вам нужно называется Dependency Injection Container. Дальше есть 3 варианта:

Почитал про контейнеры...

А что делать, если в "моей" платформе (среде разработки) нету контейнеров?

Можно ли написать свой класс типа "самопальный" контейнер?
Тогда получается, что каждый класс программы должен иметь ссылку на этот объект "самопального" контейнера?
Re[4]: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 11:38
Оценка:
Здравствуйте, ·, Вы писали:


Z>>Почитал про контейнеры...

·>Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.
Вот сколько использовал контейнеры, никогда не инициализировал их через XML. КМК, это жуткое извращение.
Жутким наследием они лично мне не кажутся, у них вполне есть своя ниша, где они удобны. Тут, главное, не увлекаться и не пихать их вообще вовсюда да ещё с интерфейсами для каждого класса.
Хотя да, когда их используют индусы — иногда становится страшно, видел такое неоднократно.
Re[3]: Инициализация приложения - внедрение зависимостей в DDD
От: zelenprog  
Дата: 10.11.23 11:38
Оценка:
Z>Можно ли написать свой класс типа "самопальный" контейнер?

Нашел вот такую статью:
https://www.wiktorzychla.com/2022/01/winforms-dependency-injection-in-net6.html

Там в качестве контейнера используется Фабрика.
Re[4]: Инициализация приложения - внедрение зависимостей в DDD
От: vmpire Россия  
Дата: 10.11.23 12:56
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Нашел вот такую статью:

Z>https://www.wiktorzychla.com/2022/01/winforms-dependency-injection-in-net6.html

Z>Там в качестве контейнера используется Фабрика.

Это второй вариант, из тех, что я перечислял
Re[5]: Инициализация приложения - внедрение зависимостей в DDD
От: · Великобритания  
Дата: 10.11.23 14:41
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.

V>Вот сколько использовал контейнеры, никогда не инициализировал их через XML. КМК, это жуткое извращение.
Ну не важно, детали имплементации. Главное проблема — словарь, который ошибки компиляции превращает в отложенные ошибки исполнения.

V>Жутким наследием они лично мне не кажутся, у них вполне есть своя ниша, где они удобны. Тут, главное, не увлекаться и не пихать их вообще вовсюда да ещё с интерфейсами для каждого класса.

V>Хотя да, когда их используют индусы — иногда становится страшно, видел такое неоднократно.
Какая ниша-то? Делать проджект более солидным?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Инициализация приложения - внедрение зависимостей в D
От: · Великобритания  
Дата: 10.11.23 14:51
Оценка:
Здравствуйте, microuser, Вы писали:

M>Ага, очень удобно потом при добавлении новой зависимости в 50 местах прописывать ее инициализацию, вот уж где "задний контейнер"

Если это обычный код, то добавление зависимости это как добавление параметра к методу. Делается элементарно, тем более с помощью современных IDE, с проверкой корректности изменений компилятором ещё в процессе набора кода.
Контейнер все проблемы отложит на этап запуска приложения, нередко только уже в проде. А так как это просто словарь, то поведение бывает недетерминировано, и бывают такие весёлости, что даже всё работает, почти всегда.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 10.11.2023 14:53 · . Предыдущая версия . Еще …
Отредактировано 10.11.2023 14:53 · . Предыдущая версия .
Re[5]: Инициализация приложения - внедрение зависимостей в D
От: · Великобритания  
Дата: 10.11.23 15:41
Оценка:
Здравствуйте, microuser, Вы писали:

>>>Контейнеры не нужны. Это жуткое наследие индусов в ентерпрайзе. Люди просто не могут освоить основной ЯП и думают, что если то же самое переписать на XML то случится чудо.

M>XML это только в богомерзкой жаве,
А эти C#-нутые поняли что XML — маловато будет и сделали ещё и json! Вот уж где православие и скрепы. Но это не предел! Рекомендую yaml.

M>в C# контейнеры изначально работали без конфигов и все прекрасно.

Ага, ага, ага, ну да. А думаешь откуда c# стырил идею контейнеров?

В качестве ликбеза. Все эти контейнеры пошли отсюда
https://www.youtube.com/watch?v=BcmUOmvl1N8

Приятного аппетита.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 10.11.2023 15:43 · . Предыдущая версия .
Re[7]: Инициализация приложения - внедрение зависимостей в DDD
От: · Великобритания  
Дата: 10.11.23 15:57
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>Ну не важно, детали имплементации. Главное проблема — словарь, который ошибки компиляции превращает в отложенные ошибки исполнения.

V>Да, есть такой недостаток. Но на практике эти ошибки довольно редки и быстро локализуются.
На моей практике — они попадают редко, но метко.

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

V>Обе они вылетают сразу в момент создания использующего объектаэ
Верно. А в нормальном wiring коде такие ошибки просто подсвечиваются красным как ошибка компиляции ещё в процессе набора кода.

V>·>Какая ниша-то? Делать проджект более солидным?

V>Те проекты, где нужно отделить логику связи объектов от логики создания объектов. Как правило, это удобно, например, в типовых web проектах на ASP.NET или в проектах с бизнес-компонентами, которые друг друга используют.
wiring код это и делает. Только это обычный код на основном ЯП, а не хитрый API конкретного фреймворка.

V>Тот пример, который Вы привели выше (императивная связь объектов) ведёт к дублированию кода

Ты всё ещё не проникся. Wiring код это самый обыкновенный код, который пишется ровно так же как и весь остальной код. Скажем, дублирование можно избежать выносом одинаковых кусков в переиспользуемый метод или, например, оборачиванием в какой-нибудь цикл.
Если ты умеешь избегать дублирование кода в принципе, то это же умение точно так же работает и для wiring-кода.

V>и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.

Эээ... Фабрика/билдер?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Инициализация приложения - внедрение зависимостей в D
От: vmpire Россия  
Дата: 10.11.23 16:10
Оценка:
Здравствуйте, vmpire, Вы писали:

V>Здравствуйте, ·, Вы писали:


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

V>>>Обе они вылетают сразу в момент создания использующего объектаэ
V>·>Верно. А в нормальном wiring коде такие ошибки просто подсвечиваются красным как ошибка компиляции ещё в процессе набора кода.
Это просто баланс интересов. Минимальное дублирование кода и отделение логики создания объектов ценой потенциальных ошибок в рантайме.

V>>>Тот пример, который Вы привели выше (императивная связь объектов) ведёт к дублированию кода

V>·>Ты всё ещё не проникся. Wiring код это самый обыкновенный код, который пишется ровно так же как и весь остальной код. Скажем, дублирование можно избежать выносом одинаковых кусков в переиспользуемый метод или, например, оборачиванием в какой-нибудь цикл.
V>·>Если ты умеешь избегать дублирование кода в принципе, то это же умение точно так же работает и для wiring-кода.
Я как раз проникся. Как только мы в этом wiring коде отделяем логику создания объектов с целью его переиспользования от логики их связи, мы как раз и получаем самый обыкновенный DI контейнер, разве что статический.
Точнее, контейнером станет набор переменных, в которых лежат созданные объекты перед тем, как их связали.
С семи же потенциальными проблемами рантайма(забыли создать объект, который потом пытаемся связать).
Тут, правда, поможет новая фишка C# про nullable reference types, но только в не очень сложных случаях.

V>>>и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.

V>·>Эээ... Фабрика/билдер?
Ну и напишете вручную тот же код, который уже есть в DI фреймворке. И в чём тогда плюсы?
Отредактировано 10.11.2023 16:11 vmpire . Предыдущая версия .
Re[10]: Инициализация приложения - внедрение зависимостей в D
От: · Великобритания  
Дата: 10.11.23 16:27
Оценка:
Здравствуйте, vmpire, Вы писали:

V>>·>Верно. А в нормальном wiring коде такие ошибки просто подсвечиваются красным как ошибка компиляции ещё в процессе набора кода.

V>Это просто баланс интересов. Минимальное дублирование кода и отделение логики создания объектов ценой потенциальных ошибок в рантайме.
Дублирование ортогонально контейнеру. Можно и написать wiring код без дублирования, а можно надублировать конфигурацию контейра. Что кстати, мне нередко доводилось наблюдать — почти одинаковые конфигурации для тестов/прода; в итоге тесты работают, а прод падает, внезапно.
Никаких осязаемых преимуществ конкретно контейнер не даёт.

V>>·>Если ты умеешь избегать дублирование кода в принципе, то это же умение точно так же работает и для wiring-кода.

V>Я как раз проникся. Как только мы в этом wiring коде отделяем логику создания объектов с целью его переиспользования от логики их связи, мы как раз и получаем самый обыкновенный DI контейнер, разве что статический.
Ну можно так выразиться, хотя неясно почему "контейнер", нет ничего контейнерного (словаря-то нет!). Это и есть хорошо, что статический. Динамика это в php и javascript, зачем это тащить в компилируемые ЯП — неясно.

V>Точнее, контейнером станет набор переменных, в которых лежат созданные объекты перед тем, как их связали.

V>С семи же потенциальными проблемами рантайма(забыли создать объект, который потом пытаемся связать).
Подавляющее большинство связывания будет через Constructor Injection. Т.е. ты тупо не сможешь создать объект в коде не имея нужных ему зависимостей.

V>Тут, правда, поможет новая фишка C# про nullable reference types, но только в не очень сложных случаях.

Ты видимо связывание через Property Injection? Тоже хак, который надо избегать.

V>>>>и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.

V>>·>Эээ... Фабрика/билдер?
V>Ну и напишете вручную тот же код, который уже есть в DI фреймворке. И в чём тогда плюсы?
Что этот код явный и работает явно. И отлаживается как обычный код, и тестами покрывается, и стектрейсы явные и т.п.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Инициализация приложения - внедрение зависимостей в D
От: vmpire Россия  
Дата: 10.11.23 16:43
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, vmpire, Вы писали:


V>>>·>Верно. А в нормальном wiring коде такие ошибки просто подсвечиваются красным как ошибка компиляции ещё в процессе набора кода.

V>>Это просто баланс интересов. Минимальное дублирование кода и отделение логики создания объектов ценой потенциальных ошибок в рантайме.
·>Дублирование ортогонально контейнеру. Можно и написать wiring код без дублирования, а можно надублировать конфигурацию контейра. Что кстати, мне нередко доводилось наблюдать — почти одинаковые конфигурации для тестов/прода; в итоге тесты работают, а прод падает, внезапно.
Вы точно не путаете проблему DI контейнера как такового с проблемой его инициализации из XML или JSON? XML/JSON это практически однозначно зло, кроме, может быть, очень редких специфичных случаев.
Если нужны одинаковые конфигурации для тестов и прода, то кто мешает слелать эту конфигурацию в одном куске кода и переиспользовать?
А если они не в одном файле, то и wiring код может отличаться.
·>Никаких осязаемых преимуществ конкретно контейнер не даёт.
В тех случаях, когда не даёт, можно ведь им не пользоваться. Я же сразу написал, что это нишевый инструмент.

V>>>·>Если ты умеешь избегать дублирование кода в принципе, то это же умение точно так же работает и для wiring-кода.

V>>Я как раз проникся. Как только мы в этом wiring коде отделяем логику создания объектов с целью его переиспользования от логики их связи, мы как раз и получаем самый обыкновенный DI контейнер, разве что статический.
·>Ну можно так выразиться, хотя неясно почему "контейнер", нет ничего контейнерного (словаря-то нет!). Это и есть хорошо, что статический. Динамика это в php и javascript, зачем это тащить в компилируемые ЯП — неясно.
Словарь — это всего лишь один из возможных механизмов реализации. Если контейнер не будет библиотекой для использования в произвольных проектах, а делается для одного раза, то словарь будет только лишним.

V>>Точнее, контейнером станет набор переменных, в которых лежат созданные объекты перед тем, как их связали.

V>>С семи же потенциальными проблемами рантайма(забыли создать объект, который потом пытаемся связать).
·>Подавляющее большинство связывания будет через Constructor Injection. Т.е. ты тупо не сможешь создать объект в коде не имея нужных ему зависимостей.
Пример:
ILogger myLogger = new MyCoolLogger();
IDatabase myDatabase = null;
switch (configuredDatabase) {
  case "mysql": myDatabase = new MySqlDatabase(myLogger); break;
  case "yoursql": myDatabase = new YourSqlDatabase(myLogger); break;
}

// Через 100 строчек кода

var myBusinessObject = new MyBusiness(myDatabase, myLogger);

По сути, точно такая же ошибка, как если бы забыли зарегистрировать объект в DI контейнере.


V>>Тут, правда, поможет новая фишка C# про nullable reference types, но только в не очень сложных случаях.

·>Ты видимо связывание через Property Injection? Тоже хак, который надо избегать.
Боже упаси, конечно, нет. Я про вот это

V>>>>>и неудобен, когда нужно закодировать разное время жизни объектов и при этом создавать их по требованию.

V>>>·>Эээ... Фабрика/билдер?
V>>Ну и напишете вручную тот же код, который уже есть в DI фреймворке. И в чём тогда плюсы?
·>Что этот код явный и работает явно. И отлаживается как обычный код, и тестами покрывается, и стектрейсы явные и т.п.
Код явный — это, конечно, хорошо. Но с таким аргументом лучше вообще библиотеками не пользоваться, а то мало ли что там внутри. Впрочем, я лично знал человека, который писал свою реализацию списка на C# толко потому, что в своей реализации он понимает, как она работает.
При отладке и содержимое контейнера прекрасно видно. А в логах — не всё ли равно какой там будет класс, свой или библиотечный?
С трейсами то же самое.
Так что на мой взгляд оно эквивалентно.
Re[12]: Инициализация приложения - внедрение зависимостей в D
От: · Великобритания  
Дата: 10.11.23 17:36
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>Дублирование ортогонально контейнеру. Можно и написать wiring код без дублирования, а можно надублировать конфигурацию контейра. Что кстати, мне нередко доводилось наблюдать — почти одинаковые конфигурации для тестов/прода; в итоге тесты работают, а прод падает, внезапно.

V>Вы точно не путаете проблему DI контейнера как такового с проблемой его инициализации из XML или JSON? XML/JSON это практически однозначно зло, кроме, может быть, очень редких специфичных случаев.
Однозначное зло — это словарь, рефлексия, строковые константы и т.п.

V>Если нужны одинаковые конфигурации для тестов и прода, то кто мешает слелать эту конфигурацию в одном куске кода и переиспользовать?

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

V>·>Никаких осязаемых преимуществ конкретно контейнер не даёт.

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

V>·>Ну можно так выразиться, хотя неясно почему "контейнер", нет ничего контейнерного (словаря-то нет!). Это и есть хорошо, что статический. Динамика это в php и javascript, зачем это тащить в компилируемые ЯП — неясно.

V>Словарь — это всего лишь один из возможных механизмов реализации. Если контейнер не будет библиотекой для использования в произвольных проектах, а делается для одного раза, то словарь будет только лишним.
А какие другие возможные механизмы? Нужна коллекция объектов, граф типов зависимостей и т.п. Это и есть контейнер.

V>·>Подавляющее большинство связывания будет через Constructor Injection. Т.е. ты тупо не сможешь создать объект в коде не имея нужных ему зависимостей.

V>Пример:
V>IDatabase myDatabase = null;

А причём тут wiring/контейнер?! За такой говнокод в любой части проекта надо сильно по голове бить. И переписать хотя бы вот так (или какой там синтаксис switch expression?):
ILogger myLogger = new MyCoolLogger();
IDatabase myDatabase = switch (configuredDatabase) {
  case "mysql" => new MySqlDatabase(myLogger);
  case "yoursql" => new YourSqlDatabase(myLogger);
  _ => throw new UnsupportedArgument("Unknown " + configuredDatabase);
}

И ещё сделать хотя бы enum для configuredDatabase. Строковые константы — тоже надо избегать, хотя вот как раз в DI-контейнерах без них часто никак, т.к. имена инстансов надо как-то использовать.

V>По сути, точно такая же ошибка, как если бы забыли зарегистрировать объект в DI контейнере.

Такая ошибка может быть в любом коде. Wiring тут не при чём.

V>·>Ты видимо связывание через Property Injection? Тоже хак, который надо избегать.

V>Боже упаси, конечно, нет. Я про вот это
= null часто можно просто не писать, так что это мелкая проблема обычно.

V>·>Что этот код явный и работает явно. И отлаживается как обычный код, и тестами покрывается, и стектрейсы явные и т.п.

V>Код явный — это, конечно, хорошо. Но с таким аргументом лучше вообще библиотеками не пользоваться, а то мало ли что там внутри. Впрочем, я лично знал человека, который писал свою реализацию списка на C# толко потому, что в своей реализации он понимает, как она работает.
V>При отладке и содержимое контейнера прекрасно видно. А в логах — не всё ли равно какой там будет класс, свой или библиотечный?
V>С трейсами то же самое.
V>Так что на мой взгляд оно эквивалентно.
Так тут не надо никакой код по сути писать. Суть того, что делает контейнер, это оборачивает обычные конструкции ЯП new/if/switch в библиотечные классы.
Зачем тебе нужнен фреймворк, чтобы написать фабрику? Фабрика это просто _один_ метод, иногда даже просто лямбда.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[12]: Инициализация приложения - внедрение зависимостей в D
От: · Великобритания  
Дата: 10.11.23 17:40
Оценка:
Здравствуйте, vmpire, Вы писали:

V>IDatabase myDatabase = null;

V>// Через 100 строчек кода
V>var myBusinessObject = new MyBusiness(myDatabase, myLogger);
Да, кстати, IDEA в таком коде скорее всего warning покажет и какой-нибудь spotbugs заагрится. В отличие от контейнеров, где только на проде и вылезет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: Инициализация приложения - внедрение зависимостей в D
От: vmpire Россия  
Дата: 10.11.23 18:53
Оценка:
Здравствуйте, ·, Вы писали:

V>>Если нужны одинаковые конфигурации для тестов и прода, то кто мешает слелать эту конфигурацию в одном куске кода и переиспользовать?

·>В этом случае придётся бороться с фреймворком как вменяемо разделять конфигурации.
В любом случае эта проблема не зависит от наличия или отсутствия DI

V>>·>Никаких осязаемых преимуществ конкретно контейнер не даёт.

V>>В тех случаях, когда не даёт, можно ведь им не пользоваться. Я же сразу написал, что это нишевый инструмент.
·>Так мой поинт, что никогда не даёт.
Так вы же сами рассматриваете контейнер как вариант реализации, если нужно выносить общую логику создания объектов либо делать разные управляемые времена жизни.
Вся разница в том, что Вы для этого не хотите использовать готовые обобщённые реализации.

V>>·>Ну можно так выразиться, хотя неясно почему "контейнер", нет ничего контейнерного (словаря-то нет!). Это и есть хорошо, что статический. Динамика это в php и javascript, зачем это тащить в компилируемые ЯП — неясно.

V>>Словарь — это всего лишь один из возможных механизмов реализации. Если контейнер не будет библиотекой для использования в произвольных проектах, а делается для одного раза, то словарь будет только лишним.
·>А какие другие возможные механизмы? Нужна коллекция объектов, граф типов зависимостей и т.п. Это и есть контейнер.
Как я писал уже в ответе ТС, простейший вариант — это тупо класс с полями, в которых либо сами реализации объектов, либо их factory functions.

V>>·>Подавляющее большинство связывания будет через Constructor Injection. Т.е. ты тупо не сможешь создать объект в коде не имея нужных ему зависимостей.

V>>Пример:
·>
V>>IDatabase myDatabase = null;
·>

·>А причём тут wiring/контейнер?! За такой говнокод в любой части проекта надо сильно по голове бить. И переписать хотя бы вот так (или какой там синтаксис switch expression?):
Это код просто для иллюстрации проблемы. Я сам прекрасно вижу его косяки.
Это упрощённый вариант для объяснения того, что где-то в создании объектов может быть ошибка, которую всё равно не отловит компилятор.

V>>По сути, точно такая же ошибка, как если бы забыли зарегистрировать объект в DI контейнере.

·>Такая ошибка может быть в любом коде. Wiring тут не при чём.
Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.

·>Так тут не надо никакой код по сути писать. Суть того, что делает контейнер, это оборачивает обычные конструкции ЯП new/if/switch в библиотечные классы.

·>Зачем тебе нужнен фреймворк, чтобы написать фабрику? Фабрика это просто _один_ метод, иногда даже просто лямбда.
Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.
По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.
Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.
Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
Если писать свою программу без всяких фреймворков, то это просто вопрос личных предпочтений.
Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
Re[14]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 10.11.23 21:30
Оценка:
Здравствуйте, vmpire, Вы писали:

V>>>Если нужны одинаковые конфигурации для тестов и прода, то кто мешает слелать эту конфигурацию в одном куске кода и переиспользовать?

V>·>В этом случае придётся бороться с фреймворком как вменяемо разделять конфигурации.
V>В любом случае эта проблема не зависит от наличия или отсутствия DI
Эээ... Не путай. DI это DI, а фреймворк это фреймворк.
Допустим ты имел в виду "не зависит от наличия или отсутствия IoC-фреймворка". Как раз зависит. Когда ты пишешь wiring code на самом ЯП — у тебя есть вся выразительная сила ЯП. Хочешь интерфейсы, хочешь классы, хочешь if/switch/циклы/методы/лямбды — полный арсенал. А с фреймворком — только полтора типичных паттерна в которые умеет фреймворк, и целая наука каких-то плагинов, магических расширений и аннотаций, и это всё может понадобиться чтобы реализовать какой-нибудь банальный switch.

V>>>В тех случаях, когда не даёт, можно ведь им не пользоваться. Я же сразу написал, что это нишевый инструмент.

V>·>Так мой поинт, что никогда не даёт.
V>Так вы же сами рассматриваете контейнер как вариант реализации, если нужно выносить общую логику создания объектов либо делать разные управляемые времена жизни.
V>Вся разница в том, что Вы для этого не хотите использовать готовые обобщённые реализации.
Угу, т.к. все эти обобщённые реализации — по сути переливание из пустого в порожнее.

V>·>А какие другие возможные механизмы? Нужна коллекция объектов, граф типов зависимостей и т.п. Это и есть контейнер.

V>Как я писал уже в ответе ТС, простейший вариант -
Простейший вариант это new IndexPageController(new ProfileService(new ProfileRepository())). Даже класс и поля необязательны.

V>это тупо класс с полями, в которых либо сами реализации объектов, либо их factory functions.

Это как-то не очень подходит под общепринятое понятие IoC-контейнер.

V>·>А причём тут wiring/контейнер?! За такой говнокод в любой части проекта надо сильно по голове бить. И переписать хотя бы вот так (или какой там синтаксис switch expression?):

V>Это код просто для иллюстрации проблемы. Я сам прекрасно вижу его косяки.
И получилась яркая иллюстрация, внезапно. Ты показал типичный код который обычно логически соответствует типичной конфигурации контейнера и сразу бросилось в глаза — какой же кривой косячный код, и насколько же он хрупкий и где что может пойти не так. Так никто вменяемый писать не будет и реьвю такое уж точно не пройдёт.
А вот если это говно завернуть блестящей обёрткой IoC-фреймворка — так внезапно — конфеткой называть начинают.

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

Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.

V>>>По сути, точно такая же ошибка, как если бы забыли зарегистрировать объект в DI контейнере.

V>·>Такая ошибка может быть в любом коде. Wiring тут не при чём.
V>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.
Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.

V>·>Так тут не надо никакой код по сути писать. Суть того, что делает контейнер, это оборачивает обычные конструкции ЯП new/if/switch в библиотечные классы.

V>·>Зачем тебе нужнен фреймворк, чтобы написать фабрику? Фабрика это просто _один_ метод, иногда даже просто лямбда.
V>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.
Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.

V>По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.

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

V>Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.

V>Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
Ты опять запутался. Это уже не DI, а печально известный Service Locator — ещё более знатный и злой антипаттерн. И вот это всё говно в комплекте с любым фреймворком. Закопать надо давно.

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

V>Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
wiring code — основан на DI тоже.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 10.11.2023 21:34 · . Предыдущая версия . Еще …
Отредактировано 10.11.2023 21:32 · . Предыдущая версия .
Re[15]: Инициализация приложения - внедрение зависимостей в
От: vmpire Россия  
Дата: 11.11.23 01:30
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, vmpire, Вы писали:


V>>>>Если нужны одинаковые конфигурации для тестов и прода, то кто мешает слелать эту конфигурацию в одном куске кода и переиспользовать?

V>>·>В этом случае придётся бороться с фреймворком как вменяемо разделять конфигурации.
V>>В любом случае эта проблема не зависит от наличия или отсутствия DI
·>Эээ... Не путай. DI это DI, а фреймворк это фреймворк.
·>Допустим ты имел в виду "не зависит от наличия или отсутствия IoC-фреймворка".
Да, именно это
·>Как раз зависит. Когда ты пишешь wiring code на самом ЯП — у тебя есть вся выразительная сила ЯП. Хочешь интерфейсы, хочешь классы, хочешь if/switch/циклы/методы/лямбды — полный арсенал. А с фреймворком — только полтора типичных паттерна в ·>которые умеет фреймворк, и целая наука каких-то плагинов, магических расширений и аннотаций, и это всё может понадобиться чтобы реализовать какой-нибудь банальный switch.
В данном подвопросе было не про гибкость, а про то, что в обоих вариантах можно забыть что-нибудь инициализировать и будет падение в рантайме.

V>>·>А какие другие возможные механизмы? Нужна коллекция объектов, граф типов зависимостей и т.п. Это и есть контейнер.

V>>Как я писал уже в ответе ТС, простейший вариант -
·>Простейший вариант это new IndexPageController(new ProfileService(new ProfileRepository())). Даже класс и поля необязательны.
Результат этого выражения нужно где-то хранить. Вот для этого поля и классы.

V>>это тупо класс с полями, в которых либо сами реализации объектов, либо их factory functions.

·>Это как-то не очень подходит под общепринятое понятие IoC-контейнер.
Специально попытался найти официальное определение — не нашёл.
Поэтому я понимаю буквально: IoC-контейнер — контейнер, хранящий объекты, используемые для реализации IoC

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

·>Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.
Ещё и не такой пишут, поверьте.

V>>>>По сути, точно такая же ошибка, как если бы забыли зарегистрировать объект в DI контейнере.

V>>·>Такая ошибка может быть в любом коде. Wiring тут не при чём.
V>>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.
·>Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.
Потому, что Вы продолжаете цепляться к конкретному коду и не хотите понимать идею. А идея в том, что инициализация может быть сложной, иметь вложенные вызовы, внешние зависимости и т.п. И в этой реализации могут быть ошибки. В том числе и не обнаруживаемые компилятором. Или обнаруживаемые, но не ошибкой, а warningом, которые индусы игнорируют.

V>>·>Так тут не надо никакой код по сути писать. Суть того, что делает контейнер, это оборачивает обычные конструкции ЯП new/if/switch в библиотечные классы.

V>>·>Зачем тебе нужнен фреймворк, чтобы написать фабрику? Фабрика это просто _один_ метод, иногда даже просто лямбда.
V>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.
·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.

V>>По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.

·>О. Спасибо, что напомнил. Знатный антипаттерн — глобальная переменная. А здесь даже публичная глобальная переменная свалка.
Вот с этим я согласен. Когда в контейнер начинают валить всё подряд — получается свалка.

V>>Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.

V>>Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
·>Ты опять запутался. Это уже не DI, а печально известный Service Locator — ещё более знатный и злой антипаттерн. И вот это всё говно в комплекте с любым фреймворком. Закопать надо давно.
Это ровно то (второй пример), чем занимается DI фреймворк.

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

V>>Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
·>wiring code — основан на DI тоже.
Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.
Re[16]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 11.11.23 18:53
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>Как раз зависит. Когда ты пишешь wiring code на самом ЯП — у тебя есть вся выразительная сила ЯП. Хочешь интерфейсы, хочешь классы, хочешь if/switch/циклы/методы/лямбды — полный арсенал. А с фреймворком — только полтора типичных паттерна в ·>которые умеет фреймворк, и целая наука каких-то плагинов, магических расширений и аннотаций, и это всё может понадобиться чтобы реализовать какой-нибудь банальный switch.

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

V>·>Простейший вариант это new IndexPageController(new ProfileService(new ProfileRepository())). Даже класс и поля необязательны.

V>Результат этого выражения нужно где-то хранить. Вот для этого поля и классы.
Ну как минимум — в локальной переменной или тупо однострочник void main(String args[]){new App(new Dependency()).start(args);}.

V>·>Это как-то не очень подходит под общепринятое понятие IoC-контейнер.

V>Специально попытался найти официальное определение — не нашёл.
Это понятие родилось где-то в Spring, стоит читать его доку.

V>Поэтому я понимаю буквально: IoC-контейнер — контейнер, хранящий объекты, используемые для реализации IoC

Ну типа того. А wiring код может ничего не хранить. Он просто соединяет компоненты.

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

V>·>Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.
V>Ещё и не такой пишут, поверьте.
Верю, конечно. Но если в проекте пишут такой код, то проблемы с wiring-ом будут незаметны на фоне всего остального.

V>>>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.

V>·>Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.
V>Потому, что Вы продолжаете цепляться к конкретному коду и не хотите понимать идею. А идея в том, что инициализация может быть сложной, иметь вложенные вызовы, внешние зависимости и т.п. И в этой реализации могут быть ошибки. В том числе и не обнаруживаемые компилятором. Или обнаруживаемые, но не ошибкой, а warningом, которые индусы игнорируют.
Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.

V>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.

V>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
V>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.

V>>>По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.

V>·>О. Спасибо, что напомнил. Знатный антипаттерн — глобальная переменная. А здесь даже публичная глобальная переменная свалка.
V>Вот с этим я согласен. Когда в контейнер начинают валить всё подряд — получается свалка.
А ты видел когда-то что-то другое? Контекст контейнера это огромный словарь всего подряд со всего приложения, и всё публично — что угодно может зависеть от чего угодно, т.к. всё в одной свалке. Возможности структуризации контекста практически отсутствуют.

V>>>Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.

V>>>Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
V>·>Ты опять запутался. Это уже не DI, а печально известный Service Locator — ещё более знатный и злой антипаттерн. И вот это всё говно в комплекте с любым фреймворком. Закопать надо давно.
V>Это ровно то (второй пример), чем занимается DI фреймворк.
Да боже. Не "DI фреймворк", а "IoC фреймворк". SL и DI это два противоположных подхода реализации IoC принципа. Не знаю что ты имеешь в виду, но в этом топике примеров кода SL не было.
"дай мне ссылку на реализацию интерфейса IБазаДанных" — это SL: var db = serviceLocator.GetService<IБазаДанных>().

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

V>>>Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
V>·>wiring code — основан на DI тоже.
V>Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.
Я не знаю что такое DI-контейнер. На всякий случай: "dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally"
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[17]: Инициализация приложения - внедрение зависимостей в
От: vmpire Россия  
Дата: 11.11.23 20:19
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, vmpire, Вы писали:


V>>·>Как раз зависит. Когда ты пишешь wiring code на самом ЯП — у тебя есть вся выразительная сила ЯП. Хочешь интерфейсы, хочешь классы, хочешь if/switch/циклы/методы/лямбды — полный арсенал. А с фреймворком — только полтора типичных паттерна в ·>которые умеет фреймворк, и целая наука каких-то плагинов, магических расширений и аннотаций, и это всё может понадобиться чтобы реализовать какой-нибудь банальный switch.

V>>В данном подвопросе было не про гибкость, а про то, что в обоих вариантах можно забыть что-нибудь инициализировать и будет падение в рантайме.
·>А я говорю о том, что с вариантом wiring кода можно написать код так, что забыть будет невозможно и падения в рантайме произойти не может. А в случае фреймворка — код написать нормально нельзя никак. В этом и есть принципиальная разница.
естественно, свой код всегда более гибкий чем любой фреймворк.

V>>·>Простейший вариант это new IndexPageController(new ProfileService(new ProfileRepository())). Даже класс и поля необязательны.

V>>Результат этого выражения нужно где-то хранить. Вот для этого поля и классы.
·>Ну как минимум — в локальной переменной или тупо однострочник void main(String args[]){new App(new Dependency()).start(args);}.
Ну, однострочник и одна переменная для сколь-нибудь крупного приложения — это несерьёзно.

V>>·>Это как-то не очень подходит под общепринятое понятие IoC-контейнер.

V>>Специально попытался найти официальное определение — не нашёл.
·>Это понятие родилось где-то в Spring, стоит читать его доку.
Читал. Именно определения не нашёл.


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

V>>·>Именно. К этому я и клоню — такой код (wiring-код в том числе) никто в здравом уме не пишет и таких ошибок просто не будет.
V>>Ещё и не такой пишут, поверьте.
·>Верю, конечно. Но если в проекте пишут такой код, то проблемы с wiring-ом будут незаметны на фоне всего остального.
Пожалуй, да.

V>>>>Именно это я и хотел продемонстрировать. Эта ошибка может быть и в wiring коде и в регистрации объектов в контейнере, поэтому с этой точки зрения без разницы, использовать контейнер или нет.

V>>·>Как видишь — эта ошибка в wiring коде стала сразу явной и обнаруживаемой на раз, если не компилятором, то хоть статическим анализом. А если использовать контейнер — то просто навернётся на проде. Спасибо, что помог доказать мой тезис Почему ты не видишь эту разницу — неясно.
V>>Потому, что Вы продолжаете цепляться к конкретному коду и не хотите понимать идею. А идея в том, что инициализация может быть сложной, иметь вложенные вызовы, внешние зависимости и т.п. И в этой реализации могут быть ошибки. В том числе и не обнаруживаемые компилятором. Или обнаруживаемые, но не ошибкой, а warningом, которые индусы игнорируют.
·>Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.
К сожалению, инициализация в тестовом режиме не проверит инициализацию в проде. Просто потом, что это другая инициализация.

V>>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.

V>>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
V>>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.
ок, скажу совсем просто: нужно передать фабрику в 2 конструктора. Для этого её нужно сохранить в переменной, так ведь? Эта переменная и будет точкой хранения.

V>>>>По сути контейнер есть ни что иное как глобальная переменная. Весь вопрос в её типизации и формате использования.

V>>·>О. Спасибо, что напомнил. Знатный антипаттерн — глобальная переменная. А здесь даже публичная глобальная переменная свалка.
V>>Вот с этим я согласен. Когда в контейнер начинают валить всё подряд — получается свалка.
·>А ты видел когда-то что-то другое? Контекст контейнера это огромный словарь всего подряд со всего приложения, и всё публично — что угодно может зависеть от чего угодно, т.к. всё в одной свалке. Возможности структуризации контекста практически отсутствуют.
Другое я видел, пожалуй, только в своих приложениях для себя.

V>>>>Если хотите её использовать в стиле "дай мне ссылку на текущюю реализацию базы данных" — тогда можно использовать просто свой класс. Плюс — наличие переменной проверяется компилятором, в простых случаях проверяется и её инициализация.

V>>>>Если хотите её использовать в стиле "дай мне ссылку на реализацию интерфейса IБазаДанных" — тогда удобнее использовать словарь или библиотечный контейнер. Плюс — проще реализовать различные времена жизни.
V>>·>Ты опять запутался. Это уже не DI, а печально известный Service Locator — ещё более знатный и злой антипаттерн. И вот это всё говно в комплекте с любым фреймворком. Закопать надо давно.
V>>Это ровно то (второй пример), чем занимается DI фреймворк.
·>Да боже. Не "DI фреймворк", а "IoC фреймворк". SL и DI это два противоположных подхода реализации IoC принципа. Не знаю что ты имеешь в виду, но в этом топике примеров кода SL не было.
·>"дай мне ссылку на реализацию интерфейса IБазаДанных" — это SL: var db = serviceLocator.GetService<IБазаДанных>().
Понятно, меня не поняли. Забей на название, проще кодом.
Первый стиль (свой класс):
class Services
{
   public IDatabase Database { get; set; }
   public ILogger Logger { get; set; }
}

Использование:
var db = myServices.Database;


Второй стиль (готовая библиотека)
class Services
{
   public T Get<T>();
}

Использование:
var db = myServices.Get<IDatabase>();



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

V>>>>Если в рамках готового фреймворка, который уже основан на DI, то разумно этот стиль и поддерживать.
V>>·>wiring code — основан на DI тоже.
V>>Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.
·>Я не знаю что такое DI-контейнер. На всякий случай: "dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally"
Имелся в виду IoC контейнер. Я иногда ошибочно пишу DI контейнер
Re[18]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 11.11.23 21:07
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>А я говорю о том, что с вариантом wiring кода можно написать код так, что забыть будет невозможно и падения в рантайме произойти не может. А в случае фреймворка — код написать нормально нельзя никак. В этом и есть принципиальная разница.

V>естественно, свой код всегда более гибкий чем любой фреймворк.
Дело не в гибкости как таковой, а в том, что фреймворк — это рантайм логика и оно падает в рантайм. Поэтому там принципиально ничего невозможно проверить компилятором.

V>>>Результат этого выражения нужно где-то хранить. Вот для этого поля и классы.

V>·>Ну как минимум — в локальной переменной или тупо однострочник void main(String args[]){new App(new Dependency()).start(args);}.
V>Ну, однострочник и одна переменная для сколь-нибудь крупного приложения — это несерьёзно.
Да не важно. В крупном проекте wiring код будет просто крупные куски собирать. А крупные куски иметь свой wiring-код и т.п. Т.е. это просто будет объекты ЯП в памяти. Никакого специальоного словаря-контейнера нет. Ничего нигде не хранится. Ну ладно, хранится в memory heap, как собственно и все другие объекты в приложении.

V>>>Специально попытался найти официальное определение — не нашёл.

V>·>Это понятие родилось где-то в Spring, стоит читать его доку.
V>Читал. Именно определения не нашёл.
Ну потому что официального особо ничего нет... Но это вроде терпимо как некое описание:
IoC creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection to manage the components that make up the application.

V>·>Верю, конечно. Но если в проекте пишут такой код, то проблемы с wiring-ом будут незаметны на фоне всего остального.

V>Пожалуй, да.
Именно, всё можно сломать и испоганить. Поэтому я и пытаюсь повернуть разговор в русло "а как же делать хорошо?". Так вот фреймворк сделать хорошо — не позволяет, принципиально. Хорошо можно делать wiring-кодом.

V>·>Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.

V>К сожалению, инициализация в тестовом режиме не проверит инициализацию в проде. Просто потом, что это другая инициализация.
Примерно то же самое можно сказать о любом коде "обработка данных в тестовом режиме не проверит обработку данных в проде. Просто потому, что это другие данные". Но ведь более менее работающий код таки умудряются иногда писать.

Так вот, инициализация это такой же код как и всё остальное. Раз умеешь качественно писать на ЯП код бизнес-логики, то в чём проблема писать код инициализации — не понимаю.

V>>>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.

V>·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.
V>ок, скажу совсем просто: нужно передать фабрику в 2 конструктора. Для этого её нужно сохранить в переменной, так ведь? Эта переменная и будет точкой хранения.
Т.е. если тебе значение sin(x) нужно передать в две функции, то его нужно сохранить в переменной. Значит нужен библиотечный или самописный контейнер для хранения sin(x)??!
Пойми простую мысль:
var y = sin(x);
var a = f1(y);
var b = f2(y);

и
var factory = new RequestContextFactory(db);
var thing1 = new Thing1(factory);
var thing2 = new Thing2(factory);

Отличается только именами символов.

V>·>А ты видел когда-то что-то другое? Контекст контейнера это огромный словарь всего подряд со всего приложения, и всё публично — что угодно может зависеть от чего угодно, т.к. всё в одной свалке. Возможности структуризации контекста практически отсутствуют.

V>Другое я видел, пожалуй, только в своих приложениях для себя.
Т.е. на практике именно глобальная свалка.

V>·>Да боже. Не "DI фреймворк", а "IoC фреймворк". SL и DI это два противоположных подхода реализации IoC принципа. Не знаю что ты имеешь в виду, но в этом топике примеров кода SL не было.

V>·>"дай мне ссылку на реализацию интерфейса IБазаДанных" — это SL: var db = serviceLocator.GetService<IБазаДанных>().
V>Понятно, меня не поняли. Забей на название, проще кодом.
V>Первый стиль (свой класс):
Это вообще неясно что такое и зачем так делать. Особенно наличие setter-ов удивляет.

V>Второй стиль (готовая библиотека)

V>var db = myServices.Get<IDatabase>();

Это и есть антипаттерн SL. Так не надо делать. Это противоположность DI.

V>>>·>wiring code — основан на DI тоже.

V>>>Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.
V>·>Я не знаю что такое DI-контейнер. На всякий случай: "dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally"
V>Имелся в виду IoC контейнер. Я иногда ошибочно пишу DI контейнер
Значит я не понял твою мысль здесь...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[19]: Инициализация приложения - внедрение зависимостей в
От: vmpire Россия  
Дата: 13.11.23 11:27
Оценка:
Здравствуйте, ·, Вы писали:

V>>>>Специально попытался найти официальное определение — не нашёл.

V>>·>Это понятие родилось где-то в Spring, стоит читать его доку.
V>>Читал. Именно определения не нашёл.
·>Ну потому что официального особо ничего нет... Но это вроде терпимо как некое описание:
·>IoC creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection to manage the components that make up the application.
Это только описание конкретной реализации. А общего термина с определением, похоже. нет

·>Поэтому я и пытаюсь повернуть разговор в русло "а как же делать хорошо?". Так вот фреймворк сделать хорошо — не позволяет, принципиально. Хорошо можно делать wiring-кодом.

Я бы сказал, фреймворк не позволяет делать так, как хочется (потому он, собственно, и фреймворк).
Делать приемлемо (путсь не хорошо) фреймворк позволяет.


V>>·>Сложная инициализация wiring-кодом пишется и тестируется гораздо проще, чем на фреймворке. Но, повторюсь, на wiring код можно хотя бы натравить инструменты и найти ошибки-варнинги. С фреймворком будет говно и никак ни проанализировать, ни исправить, единственный надёжный тест — запустить на проде и поглядеть что поломалось.

V>>К сожалению, инициализация в тестовом режиме не проверит инициализацию в проде. Просто потом, что это другая инициализация.
·>Примерно то же самое можно сказать о любом коде "обработка данных в тестовом режиме не проверит обработку данных в проде. Просто потому, что это другие данные". Но ведь более менее работающий код таки умудряются иногда писать.

·>Так вот, инициализация это такой же код как и всё остальное. Раз умеешь качественно писать на ЯП код бизнес-логики, то в чём проблема писать код инициализации — не понимаю.


V>>>>>>Чтобы написать — не нужен. А чтобы хранить ссылку на эту фабрику для её использования из разных мест нужен либо библиотечный контейнер либо самописный.

V>>·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.
V>>ок, скажу совсем просто: нужно передать фабрику в 2 конструктора. Для этого её нужно сохранить в переменной, так ведь? Эта переменная и будет точкой хранения.
·>Т.е. если тебе значение sin(x) нужно передать в две функции, то его нужно сохранить в переменной. Значит нужен библиотечный или самописный контейнер для хранения sin(x)??!
·>Пойми простую мысль:
·>
·>var y = sin(x);
·>var a = f1(y);
·>var b = f2(y);
·>

·>и
·>
·>var factory = new RequestContextFactory(db);
·>var thing1 = new Thing1(factory);
·>var thing2 = new Thing2(factory);
·>

·>Отличается только именами символов.
Именно! И вот в этом случае переменная y (или factory) и будет той самой переменной, о которой я говорю.
Это и будет примитивный IoC контейнер, в данном простейшем случае — из одной переменной.


V>>Второй стиль (готовая библиотека)

·>
V>>var db = myServices.Get<IDatabase>();
·>

·>Это и есть антипаттерн SL. Так не надо делать. Это противоположность DI.
У MS такой вариант как раз и называется Dependency Injection

V>>>>Это не важно, я про фреймворк. Если он внутри использует DI контейнер, то довольно сложно избежать его использования.

V>>·>Я не знаю что такое DI-контейнер. На всякий случай: "dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally"
V>>Имелся в виду IoC контейнер. Я иногда ошибочно пишу DI контейнер
·>Значит я не понял твою мысль здесь...
Да мысль простая. Если пишете, например, ASP.NET приложение, которое в своей основе имеет IoC контейнер с сервисами, то не использовать его не получится.
Re[20]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 13.11.23 12:38
Оценка:
Здравствуйте, vmpire, Вы писали:

V>·>Ну потому что официального особо ничего нет... Но это вроде терпимо как некое описание:

V>·>IoC creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection to manage the components that make up the application.
V>Это только описание конкретной реализации. А общего термина с определением, похоже. нет
Ну в целом ясно о чём речь. Это не строгая теория, а просто терминология.

V>·>Поэтому я и пытаюсь повернуть разговор в русло "а как же делать хорошо?". Так вот фреймворк сделать хорошо — не позволяет, принципиально. Хорошо можно делать wiring-кодом.

V>Я бы сказал, фреймворк не позволяет делать так, как хочется (потому он, собственно, и фреймворк).
V>Делать приемлемо (путсь не хорошо) фреймворк позволяет.
Ок. Это уже у кого какие стандарты качества. Я считаю, что падения в проде — неприемлимо.

V>·>Так вот, инициализация это такой же код как и всё остальное. Раз умеешь качественно писать на ЯП код бизнес-логики, то в чём проблема писать код инициализации — не понимаю.


V>·>Отличается только именами символов.

V>Именно! И вот в этом случае переменная y (или factory) и будет той самой переменной, о которой я говорю.
V>Это и будет примитивный IoC контейнер, в данном простейшем случае — из одной переменной.
Тогда это словоблудие. Любой код в котором есть локальная переменная является IoC-конейнером. Бред же.

V>·>Это и есть антипаттерн SL. Так не надо делать. Это противоположность DI.

V>У MS такой вариант как раз и называется Dependency Injection
Ты просто невнимательно читаешь. Открой секцию Recommendations и там явно написано то, что я говорю: don't invoke GetService to obtain a service instance when you can use DI instead.

V>>>Имелся в виду IoC контейнер. Я иногда ошибочно пишу DI контейнер

V>·>Значит я не понял твою мысль здесь...
V>Да мысль простая. Если пишете, например, ASP.NET приложение, которое в своей основе имеет IoC контейнер с сервисами, то не использовать его не получится.
Ну, неужели нельзя писать ASP.NET приложение в основе которого нет контейра?.. Неуежли платформа окончательно заиндусятилась?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Инициализация приложения - внедрение зависимостей в DDD
От: zelenprog  
Дата: 14.11.23 05:46
Оценка:
Z>>Можно ли написать свой класс типа "самопальный" контейнер?
V>Конечно, это совсем несложно. Потому их уже и написали кучу.
V>Простейший контейнер — это словарь, где ключ — тип интерфейса, а значение — экземпляр реализующего класса или функция его создания.
V>А если список типов фиксирован, а универсальность контейнера не нужна, то ещё проще, даже без словаря. Просто обычный класс или структура с полями, указывающими на текущие реализации нужных интерфейсов.

Z>>Тогда получается, что каждый класс программы должен иметь ссылку на этот объект "самопального" контейнера?

V>каждый, где нужно его содержимое.

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

Дело в том, что в "моей" среде нету статических классов, которые были бы доступны отовсюду
Re[17]: Инициализация приложения - внедрение зависимостей в
От: zelenprog  
Дата: 14.11.23 06:26
Оценка:
V>>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.
V>>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.

Это будет какая-то одна "глобальная" Фабрика?
Или надо сделать много фабрик — по фабрике на каждую зависимость?
Re[21]: Инициализация приложения - внедрение зависимостей в
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.11.23 06:50
Оценка:
Здравствуйте, ·, Вы писали:

·>Ну, неужели нельзя писать ASP.NET приложение в основе которого нет контейра?

Да, DI — часть ASP.NET
Re: Инициализация приложения - внедрение зависимостей в DDD
От: zelenprog  
Дата: 14.11.23 07:41
Оценка:
Вынесу обсуждение следующего момента в отдельную ветку.

V>>·>Где хранить? Зачем? Фабрика это по сути например Func<HttpRequest, RequestContext> и собственно всё.

V>>Ну как зачем? А как эту фабрику использовать? Эту Func<> ведь нужно как-то вызвать, правильно? А для этого присвоить переменной.
·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.

Это будет какая-то одна "глобальная" Фабрика?
Или надо сделать много фабрик — по фабрике на каждую зависимость?

Прочитал по этой проблеме вот эту статью:
https://www.wiktorzychla.com/2012/12/di-factories-and-composition-root.html

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

Вот цитата из статьи:

... this is the core of the problem. By having multiple services in your system and multiple dependencies between various classes, this approach would end up in an object graph where each class depends on all possible services because possibly one of its dependant classes (or the class two, three or more nodes away in the dependency graph) depends on it!

Can you see it? You just can’t rethrow the responsibility to satisfy class dependencies to the client of the class because the client has its clients and they have their clients, too. The sole point of writing this entry is to show how to stop this madness of endless explicit dependencies which would flood your system.

...

And to cope with the issue we are going to introduce a factory. The factory will let its clients to create instances of the service. But we don’t want a concrete factory as it would end up with one, specific way of providing instances. Instead, we want the factory to be parametrized. We want a concrete implementation of the factory to be provided for the system.


В итоге все равно приходим к тому, что нужна какая-то глобальная переменная, в которой все это "настраивается".
Верно?
Re[18]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 09:26
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>·>Чё? wiring-код ничего не вызывает, кроме конструкторов (ну ладно, ещё может билдеры сложных компонент). Вызовы логики происходят внутри связываемых компонент и ни в wiring-коде, ни в конфиге фреймворка их быть не должно. Т.е. в wiring-коде эту фабрику не надо использовать — её надо только создать и передать конструкторам других компонент.

Z>Это будет какая-то одна "глобальная" Фабрика?
Z>Или надо сделать много фабрик — по фабрике на каждую зависимость?
Нет, конечно. Фабрика в данном случае — для создания контекста запроса.
Читай как и для чего применяется https://en.wikipedia.org/wiki/Factory_method_pattern
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 14.11.2023 9:29 · . Предыдущая версия .
Re[22]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 09:27
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Ну, неужели нельзя писать ASP.NET приложение в основе которого нет контейра?

G>Да, DI — часть ASP.NET
Сочувствую. Даже на Spring можно писать нормально, без использования IoC-контейнера.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[23]: Инициализация приложения - внедрение зависимостей в
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.11.23 10:03
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>·>Ну, неужели нельзя писать ASP.NET приложение в основе которого нет контейра?

G>>Да, DI — часть ASP.NET
·>Сочувствую. Даже на Spring можно писать нормально, без использования IoC-контейнера.
Не очень понято что значит "нормально"? Как передавать общий функцонал в разные компоненты? Синглтонам?
Re[24]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 13:31
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>·>Ну, неужели нельзя писать ASP.NET приложение в основе которого нет контейра?

G>>>Да, DI — часть ASP.NET
G>·>Сочувствую. Даже на Spring можно писать нормально, без использования IoC-контейнера.
G>Не очень понято что значит "нормально"? Как передавать общий функцонал в разные компоненты? Синглтонам?
Через параметры конструктора же. Просто явно, с проверкой компилятором, а не магией контейнера.
var commonDb = new Database(config);
var myBusinessObject1 = new MyBusiness1(commonDb);
var myBusinessObject2 = new MyBusiness2(commonDb, config);
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[25]: Инициализация приложения - внедрение зависимостей в
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.11.23 14:10
Оценка:
Здравствуйте, ·, Вы писали:

G>>Не очень понято что значит "нормально"? Как передавать общий функцонал в разные компоненты? Синглтонам?

·>Через параметры конструктора же. Просто явно, с проверкой компилятором, а не магией контейнера.
·>
·>var commonDb = new Database(config);
·>var myBusinessObject1 = new MyBusiness1(commonDb);
·>var myBusinessObject2 = new MyBusiness2(commonDb, config);
·>

Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете, а добавление сервиса станет адом.
Собственно поэтому и появились синглтоны в плюсах, а в языках с рефлексией и метаданными — DI.
Re: Инициализация приложения - внедрение зависимостей в DDD
От: Разраб  
Дата: 14.11.23 14:22
Оценка:
Здравствуйте, zelenprog, Вы писали:


Z>Но там используется незнакомая мне технология.


Z>Как правильно надо сделать?


https://smarly.net/dependency-injection-in-net
https://fsharpforfunandprofit.com/posts/dependencies/

а вообще, да, в самом простом исполнении: апп держит ссылки на все модули, модули через апп могут найти(по имени, типу и т.п.) нужный модуль.
я так делал на C# работало как часы.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[26]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 14:27
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете,

Это путь надёжного кода. А что плохого в большом количестве параметров? Хорошо в том, что код проверяется компилятором, удобно навигируется в IDE и даже если вдруг понадобилось — проходится отладичком и т.п.

G>а добавление сервиса станет адом.

Не станет. С чего вдруг? Хотя если у вас у каждого сервиса десятки зависимостей, то да.. Но это не "станет адом", а покажет какой же у вас ад в архитектуре.

G>Собственно поэтому и появились синглтоны в плюсах, а в языках с рефлексией и метаданными — DI.

Синглтоны появились как логическое продолжение глобальных переменных, думаю из тех времён, когда локальные переменные ещё не придумали.
Ты тоже путаешь DI и IoC-конейнер. DI — нужен и ему не нужна рефлексия. Мой код выше — с DI. IoC-конейнер — не нужен.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 14.11.2023 14:28 · . Предыдущая версия .
Re[27]: Инициализация приложения - внедрение зависимостей в
От: Разраб  
Дата: 14.11.23 14:33
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете,

·>Это путь надёжного кода. А что плохого в большом количестве параметров?

Зачему, что параметр может быть один(типа конекста). кложуристы так любят делать.
их хеш-мапа форсит так делать, ибо легко ключики добавляются.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[27]: Инициализация приложения - внедрение зависимостей в
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.11.23 14:37
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете,

·>Это путь надёжного кода.
Ага, и его огромного количества

·>А что плохого в большом количестве параметров?

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

·>Хорошо в том, что код проверяется компилятором, удобно навигируется в IDE и даже если вдруг понадобилось — проходится отладичком и т.п.

В asp.net тоже все хорошо работает.

G>>а добавление сервиса станет адом.

·>Не станет. С чего вдруг? Хотя если у вас у каждого сервиса десятки зависимостей, то да.. Но это не "станет адом", а покажет какой же у вас ад в архитектуре.
С того, что если тебе понадобилось добавить сервис, то надо сделать в дух местах: в конструкторе самого класса и в месте его создания.
Кроме того, в веб-пиложенияъ время жизни объекта может быть разное и, соотвественно, код создания объектов будет в нескольких местах. И тебе надо еще не перепутать куда добавлять.

G>>Собственно поэтому и появились синглтоны в плюсах, а в языках с рефлексией и метаданными — DI.

·>Синглтоны появились как логическое продолжение глобальных переменных, думаю из тех времён, когда локальные переменные ещё не придумали.
Интересно когда по твоему придумали локальные переменные, паттерны и языки высокого уровня.

·>Ты тоже путаешь DI и IoC-конейнер. DI — нужен и ему не нужна рефлексия. Мой код выше — с DI. IoC-конейнер — не нужен.

DI в масштабе даже веб-фреймфорка без контейнера, который этот DI автоматизирует, не взлетит. Ну просто синглтоны везде будут (как в ASP.NET до MVC, OWIN и Core).
Re[28]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 20:10
Оценка:
Здравствуйте, Разраб, Вы писали:

G>>>Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете,

Р>·>Это путь надёжного кода. А что плохого в большом количестве параметров?
Р>Зачему, что параметр может быть один(типа конекста). кложуристы так любят делать.
Р>их хеш-мапа форсит так делать, ибо легко ключики добавляются.
Та же глобальная переменная, вид в профиль. Непонятно зачем так делать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[28]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 14.11.23 20:25
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Это путь надёжного кода.

G>Ага, и его огромного количества
Откуда огромное количество?

G>·>А что плохого в большом количестве параметров?

G>В том что это код, который нужно поддерживать и он может в том числе ошибки содержать.
Код с фреймворком тоже надо поддерживать. Внезапно. И ошибки там искать на порядок сложнее.

G>·>Хорошо в том, что код проверяется компилятором, удобно навигируется в IDE и даже если вдруг понадобилось — проходится отладичком и т.п.

G>В asp.net тоже все хорошо работает.
Ага. Как всегда — почти.

G>>>а добавление сервиса станет адом.

G>·>Не станет. С чего вдруг? Хотя если у вас у каждого сервиса десятки зависимостей, то да.. Но это не "станет адом", а покажет какой же у вас ад в архитектуре.
G>С того, что если тебе понадобилось добавить сервис, то надо сделать в дух местах: в конструкторе самого класса и в месте его создания.
И? Две строчки кода. IDE это делает автоматически, ибо это классический рефакторинг introduce parameter.

G>Кроме того, в веб-пиложенияъ время жизни объекта может быть разное и, соотвественно, код создания объектов будет в нескольких местах.

Прям ужас! Надо же — разные вещи предлагается раскладывать в разные места! Слишком не по-индусячьи что-ли?

G>И тебе надо еще не перепутать куда добавлять.

Если перепутаешь — код тупо не скомпилится. В случае фреймворка — падение будет в рантайме.

G>>>Собственно поэтому и появились синглтоны в плюсах, а в языках с рефлексией и метаданными — DI.

G>·>Синглтоны появились как логическое продолжение глобальных переменных, думаю из тех времён, когда локальные переменные ещё не придумали.
G>Интересно когда по твоему придумали локальные переменные, паттерны и языки высокого уровня.
Я ещё тогда не родился.

G>·>Ты тоже путаешь DI и IoC-конейнер. DI — нужен и ему не нужна рефлексия. Мой код выше — с DI. IoC-конейнер — не нужен.

G>DI в масштабе даже веб-фреймфорка без контейнера, который этот DI автоматизирует, не взлетит.
Взлетают и летают.

G>Ну просто синглтоны везде будут (как в ASP.NET до MVC, OWIN и Core).

Если синглтоны не писать, то сами они не появятся.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[29]: Инициализация приложения - внедрение зависимостей в
От: Разраб  
Дата: 15.11.23 00:17
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Разраб, Вы писали:


G>>>>Это пусть в никуда. Параметров станет слишком много раньше чем вы рассчитываете,

Р>>·>Это путь надёжного кода. А что плохого в большом количестве параметров?
Р>>Зачему, что параметр может быть один(типа конекста). кложуристы так любят делать.
Р>>их хеш-мапа форсит так делать, ибо легко ключики добавляются.
·>Та же глобальная переменная, вид в профиль. Непонятно зачем так делать.

есть на ютубе некто николай рыжиков, говорит красиво. утверждает, что не он первый начал))

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

Замечу, что ООП-архитектуры часто сильно осложняют реализацию из-за привычки все время скрывать данные.
хотя по адресу всегда можно любую область памяти достать.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[30]: Инициализация приложения - внедрение зависимостей в
От: · Великобритания  
Дата: 15.11.23 17:51
Оценка:
Здравствуйте, Разраб, Вы писали:

Р>>>Зачему, что параметр может быть один(типа конекста). кложуристы так любят делать.

Р>>>их хеш-мапа форсит так делать, ибо легко ключики добавляются.
Р>·>Та же глобальная переменная, вид в профиль. Непонятно зачем так делать.
Р>есть на ютубе некто николай рыжиков, говорит красиво. утверждает, что не он первый начал))
Р>принцип "открытый мир". т.к. кложа динамическая, разраб берет из мапы то что ему нужно(или кладет), не задумываясь о постороннем.
Именно, что динамическая. Там вопрос надёжности кода решается прогоном тестов. Я же говорю о wiring code как возможность иметь компайл-тайм проверки.

Использование IoC-фреймворков в ЯП со статической типизацией — это шаг назад, т.к. проверки уходят в рантайм. Но людям нравится, видимо, ловить ошибки в проде.

Р>надежность проверяется такой фичей как спецификации. точно не помню, но кажись они как тесты прогоняются в дебаге только.

Именно. И падает в проде, если правильный тест забыли написать.

Р>как результат — кода меньше, фичы разрабатываются быстрей.

Классическая троица: быстро, качественно, недорого. "Быстро" — ты уже использовал, получается что на кложе код либо некачественный, либо дорогой.

Р>Замечу, что ООП-архитектуры часто сильно осложняют реализацию из-за привычки все время скрывать данные.

В смысле сложнее выстрелить в ногу? Так это же хорошо.

Р>хотя по адресу всегда можно любую область памяти достать.

Ну от ЯП зависит, в яве адресов-то нет. Так что никак не достать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Инициализация приложения - внедрение зависимостей в DDD
От: AndrewN Россия  
Дата: 20.11.23 13:35
Оценка:
Здравствуйте, zelenprog, Вы писали:


Z>Здравствуйте!

Z>Подскажите как осуществляется начальная инициализация ("сборка") всех зависимостей в приложении, построенном с помощью подхода DDD?

По-моему, вы описали как работает фреймворк Spring в Java https://spring.io
Там сам фреймворк управляет объектами ("бинами"), отслеживает зависимости и внедряет то, что нужно и куда нужно.
--------------------------------------------------------------
Правильно заданный вопрос содержит в себе половину ответа
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.