Здравствуйте, varenikAA, Вы писали:
VD>>Подумай на досуге, как можно создавать сервисы не зная о их зависимостях и порядке создания. AA>Это просто. "Отказ от зависимости" и переход к "функциональной композиции".
Т.е. надо просто описать все зависимости и порядок создания.
AA>Марк Земанн (автор DI) как раз сейчас проповедует "отказ" https://blog.ploeh.dk/2017/02/02/dependency-rejection/. AA>Правда для этого необходим соответствующий ЯП, наверно.
Что-то я не понял смысл этой статьи... Это не про dependencies как таковые, а про разделение pure и impure-функций.
AA>Еще в статье заметно, что частичное применение это то же DI только в ФП стиле.
И собственно всё стоило говорить в теме DI в ФП стиле.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, varenikAA, Вы писали:
VD>>>Подумай на досуге, как можно создавать сервисы не зная о их зависимостях и порядке создания. AA>>Это просто. "Отказ от зависимости" и переход к "функциональной композиции". ·>Т.е. надо просто описать все зависимости и порядок создания.
Конечно нет. функциональной композиция, на мой взгляд, это абсолютное разделение зависимостей.
int add2 (int a) => a + 2;
int sum (a , Func<int,int> add) {
return a + add(a); // <= вот мы внедрли зависимость в sum, неважно объект это, интерфейс или функция
}
int add2 (int a) => a + 2;
int sum (a , int add) {
return a + add; // <= нет зависимости, чистая функция, легко использовать, меньше магии
}
var init = 1;
var result1 = add2(init);
var result2 = sum (init, result2);
Здравствуйте, varenikAA, Вы писали:
AA> ·>Т.е. надо просто описать все зависимости и порядок создания. AA> Конечно нет. функциональной композиция, на мой взгляд, это абсолютное разделение зависимостей.
Это какой-то игрушечный пример. В простых случаях можно делать просто. Вообще зачем это всё, пиши сразу void main() {print("result")} и не мучайся
AA> int add2 (int a) => a + 2;
В реальном коде это может быть некая сущность со смыслом предметной области.
Примени свою магию!
AA> return a + add; // <= нет зависимости, чистая функция, легко использовать, меньше магии
Вопрос не про меньше магии, а о смысле магии и где именно эта магия будет находиться.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ночной Смотрящий, Вы писали:
НС>>Тебе тогда тот же вопрос. Какая обязательная фича DI контейнеров требует " расчет графа зависимостей"?
VD>А ты попробуй представить упрощенный алгоритм создания экземпляра класса. Вот есть у тебя классы A, B и C. Они зависят один от другого. Скажем C от B и B от A. Как создать C?
Ну... Очень просто.
Помечаем C как "under construction"
Смотрим, как его вообще можно создать.
Видим, что нужно B
Видим, что B у нас еще нет
Рекурсивно выполняем этот же алгоритм
Создаем C
Успешно заносим C в реестр
Если вдруг в процессе создания наткнулись на "under construction", есть два варианта. Если зависимость идет через конструктор — пишем ошибку. Если зависимость идет через поле — записываем сеттер, который нужно выполнить после инициализации зависимости.
Никаким "расчетом графа зависимостей" здесь даже не пахнет. Есть что-то неявное, но до графа ему далеко. Причем так делает аж целый Spring — de-facto standard container в мире java!
Почему знаю... В прошлом году аккуратно копался палочкой в легаси. Ну и что-то там менял. Заодно менял property injeciton на constructor injection, чтобы уменьшить количество циклических зависимостей. И вот в какой-то момент запуск (injection) стало падать. Сначала — под windows (на linux и mac — нормально). Потом и на некоторых маках. И ругалось оно на "циклическую зависимость" (бонусом, Spring вываливает весь стек и даже не пытается показать, где там начинается цикл). Что я могу сказать? Ну была там циклическая зависимость, да. Но если построить граф, она прекрасно разрешалась. По конструкторам был ясен порядок создания, остальное (properties) можно было доинициализировать. А вот если пытаться наивно создать с произвольного объекта — все с некоторой вероятностью падает.
Так что не стоит надеяться на то, что в контейнере грамотно реализованы подходы. Потому что графы — это не модно, а вот модификация байткода в процессе загрузки (которую Spring тоже делает) — самый шик.
Вероятно, есть более правильно реализованные контейнеры. Но это же нужно тратить время на изучение контейнера. На изучение его граблей. На обход его граблей. Затем на обход сложностей реализации декораторов (они очень плохо контейнерами инжектятся). Потом на обход еще чего-нибудь...
Здравствуйте, varenikAA, Вы писали:
AA>·>Вопрос не про меньше магии, а о смысле магии и где именно эта магия будет находиться. AA>Уже писал, что вопрос изначально хайповый. AA>В чем полезность Poor man's DI?
Вроде уж сто раз обсудили.. Попробую подытожить:
Отделение логики создания компонент от логики их использования и явное описание их взаимосвязей без потери Compile-time проверки зависимостей.
Родная поддержка навигации по коду и рефакторингов в IDE.
Позволяет строго структурировать зависимости и избегать ошибок случайного создания плохих зависимостей.
Живая документация по структуре приложения.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, maxkar, Вы писали:
M>Ну... Очень просто.
M>*] Помечаем C как "under construction" M>*] Смотрим, как его вообще можно создать. M>*] Видим, что нужно B M>*] Видим, что B у нас еще нет M>*] Рекурсивно выполняем этот же алгоритм M>*] Создаем C M>*] Успешно заносим C в реестр
Поздравляю тебя. Ты сейчас описал метод построения графа зависимостей. Замени объекты на вершины, а "under construction" на флаги пометки обхода и вуаля.
Собственно проблема в том, что в таком подходе проблемы вявляются в рантайме, что плохо. Чем раньше известно о проблеме, тем проще и дешевле ее устранить.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ·, Вы писали:
·>Отделение логики создания компонент от логики их использования и явное описание их взаимосвязей без потери Compile-time проверки зависимостей.
Логика отделяется при помощи DI FW, запрет null в C# 8 выполнит проверку в режиме компиляции.
·>Родная поддержка навигации по коду и рефакторингов в IDE.
Сложно судить о ваших предпочтениях, но меня из коробки vs 2019 устривает, можно найти любую ссылку, причем поиск учитывает положение курсора.
·>Позволяет строго структурировать зависимости и избегать ошибок случайного создания плохих зависимостей.
что за плохие зависимости?
·>Живая документация по структуре приложения.
Особой разницы нет, разве что ответственность за создание экземпляров ложится целиком на кодера вместо отлаженной и удобной библиотеки.
Итог: больше кода и ручное управление. Оба момента давно уже признаны признаком плохого кода.
Здравствуйте, varenikAA, Вы писали:
AA>Итог:
Мы всё это уже обсудили по нескольку раз. Мне лень повторяться ещё раз. Перечитай топик и отвечай на конкретные сообщения, если есть какие-то возражения.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, maxkar, Вы писали:
M>Никаким "расчетом графа зависимостей" здесь даже не пахнет. Есть что-то неявное, но до графа ему далеко. Причем так делает аж целый Spring — de-facto standard container в мире java!
Да почти все реальные контейнеры так делают. Поэтому забавно слышать сразу от нескольких человек про какие то рассчеты графов.
Здравствуйте, VladD2, Вы писали:
VD>Собственно проблема в том, что в таком подходе проблемы вявляются в рантайме, что плохо.
Влад, на практике нет таких проблем. Вот в текущем проекте за полтора года, не смотря на интенсивное использование DI, такой проблемы не было ни разу. Реальные проблемы DI расположены в совсем других местах, и это ни разу не "рассчет графа зависимостей".
SD>>Более удобным подходом было бы уметь генерировать граф зависимостей путем непосредственного анализа исходного кода. НС>Для чего более удобным?
Для тестирования, рефакторинга, и просто понимания — что от чего зависит, и где что сломается, если поменять вот этот компонент.
SD>> Однако там есть свои грабли, например, как понимать такую зависимость: SD>> if (random(100) < 50) then add_dependency_on(AnotherComponent) НС>А как такая зависимость, по твоему, понимается в случае DI? Разница тут будет только в случае вызова метода Resolve, что тут все заклеймили позором как антипаттерн..
Да мало ли что где клеймят. Если у тебя есть только одна сущность — "name" — а для работы с реальным миром нужно этот "name" во что-то развернуть, то хошь как хошь, а "Resolve" придется реализовать. Вопрос только в том, когда Resolve будет работать. Если во время компиляции — все чудесно, можно обложить тестами, можно научить IDE ходить по ссылками.
А если в рантайме, да где-то в каком-нибудь "DI container", да еще и на другом языке программирования (а то и вовсе на другой машине, — привет, DNS и аналогичные механизмы) — проще повеситься.
"Авторы DI", прямо скажем, умерли еще до того, как Марк начал писать.
Но удивительно, что у него заняло столько лет понять очевидную истину про implicit/explicit dependencies.
Прямо хоть самому начинать книжки писать. Нешто в самом деле очевидные вещи нужно разжевывать.
M>Никаким "расчетом графа зависимостей" здесь даже не пахнет.
Пахнет, конечно — это как раз и есть самый примитивный (не значит что плохой!) механизм расчета. Занятно, что именно так оно много где реализовано, скажем, в том же Erlang'e, весь алгоритм — 10 строчек.
Здравствуйте, SkyDance, Вы писали:
SD>Для тестирования, рефакторинга, и просто понимания — что от чего зависит, и где что сломается, если поменять вот этот компонент.
Это должен делать DI контейнер?
НС>>А как такая зависимость, по твоему, понимается в случае DI? Разница тут будет только в случае вызова метода Resolve, что тут все заклеймили позором как антипаттерн.. SD>Да мало ли что где клеймят.
Предлагаешь отвечать без привязки к сообщению, на которое отвечаешь?
Как именно? Как DI container сможет протестировать какой-то вполне конкретный экземпляр того, что в итоге получитс?
НС>Предлагаешь отвечать без привязки к сообщению, на которое отвечаешь?
Не понимаю контекста этого комментария.
На настоящий момент мои знания про DI containers в основном ограничиваются всякими вариантами из мира Java, типа тех же Spring. И там я очень хорошо знаю, как за 15 минут настрочить такое, что тестировать будет куда сложнее, чем нафиг переписать без DI.
Здравствуйте, SkyDance, Вы писали:
НС>>Предлагаешь отвечать без привязки к сообщению, на которое отвечаешь? SD>Не понимаю контекста этого комментария.
А я не понимаю, как можно в обсуждении неправильности service locator и правильности DI игнорировать заявления о неправильности service locator.
SD>На настоящий момент мои знания про DI containers в основном ограничиваются всякими вариантами из мира Java, типа тех же Spring. И там я очень хорошо знаю, как за 15 минут настрочить такое, что тестировать будет куда сложнее, чем нафиг переписать без DI.
Это можно сказать про любую более менее сложную технологию.
Здравствуйте, VladD2, Вы писали:
VD>Поздравляю тебя. Ты сейчас описал метод построения графа зависимостей. Замени объекты на вершины, а "under construction" на флаги пометки обхода и вуаля. VD>Собственно проблема в том, что в таком подходе проблемы вявляются в рантайме, что плохо. Чем раньше известно о проблеме, тем проще и дешевле ее устранить.
Это да, но это происходит при запуске (должно, по крайней мере, происходить), поэтому если что, то
приложение сразу упадет.
Здравствуйте, Sharov, Вы писали:
S>Это да, но это происходит при запуске (должно, по крайней мере, происходить),
Запуск приложения — это тоже весьма не малый процесс, если речь идет о более менее большом приложении.
Кроме того этому предшествует сборка и деплоймент. В нашем случае, например, это очень дорогие поцессы. Продукт нельзя ставить локально. Только на вириален. А его сборка с тестами может занимать часы.
S>поэтому если что, то приложение сразу упадет.
Тоже не совсем так. К сожалению работа DI переплетена с обычным кодом инициализации. Для ускорения загрузки могут использовать потоки. В результате возникают баги, которые приходится анализировать часами, а то и днями.
Если же зависимости разруливались бы во время компиляции, ошибка выявлялась бы сразу.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.