Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Sharov, Вы писали:
S>>Это именно проблема IoC/DI, т.к. он может конфигурироваться из файла. При D. Inversion и без D. Injection у нас все равно как минимум S>>одно использование типа в коде будет (кто-то же должен его создать?). А при создание может конфигурироваться хрен знает где S>>и хрен знает как, и студия может об этом вообще ничего не знать. Вот и показывает, что тип нигде не используется, хотя S>>он явно прописан в конфиге как зависимость.
VD>Что за "логика"? То что что-то там может — это не значит, что это является причиной. Ты создавая экземпляр можешь значительно больше чем любой фрэймворк. У нас вот проекте конфигурация из файла не используется в принципе. Вся конфигурация в коде.
А если не в коде, как у ТС?
VD>Если у тебя в коде интерфейс, ты автоматически получаешь большую неопределенность нежели при работе с конкретным типом, так как ты работаешь с абстракцией. Работу с абстракцией выбираешь ты сам. Если этот выбор сделан осознанно и грамотно, а не потому, что "все так делают", то ты преследовал какую-то цель. И вот эта вот цель подразумевает подмену типа в рантайме. Используя DI ты волен создавать не только экземпляры конкретных типов, но и даже делать их все sealed, чтобы гарантировать, что они не могут быть подменены в рантайме.
VD>Так что не говори чушь. Причина именно dependency inversion. Ты путаешь причину со следствием.
Речь о том, что ТС поудалял кучу классов (или собирался это сделать), т.к. R# показал, что они нигде не используются.
Потом выяснилось, что они используются, только слишком неявно, в конфиге, т.к. работают через IOC контейнер. С
этого данная тема по сути и началась. Так что причина именно Injection, а не Inversion.
Здравствуйте, Sharov, Вы писали:
S>А если не в коде, как у ТС?
У ТС DI не используется по причини не любви к ним. А все остальное попытка подобрать аргументы к исходной установке.
S>Речь о том, что ТС поудалял кучу классов (или собирался это сделать), т.к. R# показал, что они нигде не используются. S>Потом выяснилось, что они используются, только слишком неявно, в конфиге, т.к. работают через IOC контейнер. С S>этого данная тема по сути и началась. Так что причина именно Injection, а не Inversion.
Во-первых, это слишком вольная интерпретация сказанного.
Во-вторых, этого можно добиться и создавая тип скажем через активатор.
В-третьих, люди использующие DI должны понимать что происходит и производить поиск другими методами. В нашем случае, например, ищутся вызовы "...Register<НужныйТип>(...". Сам DI не знает что подсунуть вместо интерфейса. Стало быть без регистрации не обойтись. Ну, а если это конкретный sealed тип и в регистрации он не замечен, то он тупо создается через DI.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>>Так что не говори чушь. Причина именно dependency inversion. Ты путаешь причину со следствием.
S>Речь о том, что ТС поудалял кучу классов (или собирался это сделать), т.к. R# показал, что они нигде не используются. S>Потом выяснилось, что они используются, только слишком неявно, в конфиге, т.к. работают через IOC контейнер. С S>этого данная тема по сути и началась. Так что причина именно Injection, а не Inversion.
ну так это элементарно лечится: code-based configuration — "это то, что доктор прописал",
ну а уж если сам хочешь извращений, их тоже есть: и xml-based configuration, и convention over configuration...
AA>по мне так это философия. как только нужно расширить поведение объекта, так сразу возникает необходимость интерфейса и следовательно фабрики.
Не вижу связи. Фабрика — это лишь способ добавить turing-full код туда, где изначально его не было. А DI — это способ вернуть с уровня turing full на DSL (см. configuration complexity clock). И то, и другое — в runtime, то есть поздно и не особо безопасно.
Здравствуйте, SkyDance, Вы писали:
AA>>по мне так это философия. как только нужно расширить поведение объекта, так сразу возникает необходимость интерфейса и следовательно фабрики.
SD>Не вижу связи. Фабрика — это лишь способ добавить turing-full код туда, где изначально его не было. А DI — это способ вернуть с уровня turing full на DSL (см. configuration complexity clock). И то, и другое — в runtime, то есть поздно и не особо безопасно.
Считаю тема ради хайпа.
Лично мое мнение, выучи все подходы, а потом забудь все и пиши сообразуясь с ситуацией.
AA>Лично мое мнение, выучи все подходы, а потом забудь все и пиши сообразуясь с ситуацией.
Выше по теме уже выразили мнение, с которым я особенно согласен: лучший код — тот, который сразу понятно что делает. А это, в свою очередь, такой код, где минимум уровней абстракции, через которых нужно продраться.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Somescout, Вы писали:
IT>>>При желании можно абстрагироваться даже от интерфейсов. Но я не буду говорить как, не дай боже вам это понравится. S>>Само собой не будете: вы уже который комментарий стесняетесь не то что код приводить, но даже хоть чуть-чуть в конкретику углубляться.
IT>Вроде как код собираешься приводить ты, а не я.
Вы б документацию к своему LinqToDB с таким же энтузиазмом пилили, как балаболили на форуме.
Здравствуйте, VladD2, Вы писали:
VD>Лично я обычно с неохотой иду на использование DI. Не поверите, но код отлично можно писать в процедурном стиле.
Рискую быть непонятым, но скажу, что DI это даже круче — в некотором роде это ФП-стиль построения большого приложения. Как в ФП мы передаем замыкание ("настроенную" функцию), так в DI мы передаем зависимость (уже "настроенный" сервис).
·>То добавить dao1 в Svc2 никаких дополнительных правок не потребует.
Разве, а конструктор? Впрочем, не важно.
·>Зато в Dao1 добавить svc1 у тебя просто не получится скомпилировать.
Не получится, да. А при использовании ioc-контейнера может и получиться (смотря как конфигурация описана).
·>Далее по тексту начинался новый параграф.. я не понял в чём "но".
Ну, перед этим по тексту ) Я по смыслу имел в виду — в "автоматическом" управлении зависимостями (контейнер) vs "статическом" есть свои плюсы и минусы, и возникновение ошибки в рантайме минус, конечно — но нужно учитывать, что "рантайм" тут не тот, который будет долго ждать своего часа.
В целом, для себя дискуссию по ioc-контейнерам считаю законченной, дополнительные выводы по ним сделал, и спасибо всем за мнения. С ними так или иначе приходится жить частенько (по различным причинам) — и ортодоксально отовсюду их выпилить не всегда возможно, да и не всегда оправданно. Однако, при неких удобствах они, за исключением некоторых специфичных применений (описанных в т.ч. VladD2) ухудшают качество дизайна, и это необходимо понимать. Это так, немного банальностей в качестве резюме для себя.
Здравствуйте, SkyDance, Вы писали:
SD>Выше по теме уже выразили мнение, с которым я особенно согласен: лучший код — тот, который сразу понятно что делает. А это, в свою очередь, такой код, где минимум уровней абстракции, через которых нужно продраться.
Абстракции позволяют создавать нам сложные системы. Через абстракции не надо продираться, их надо принять (понять), не залезая внутрь. На то они и абстракции.
Про DI добавлю, что когда используешь DI тебе приходится разделять код на независимые компоненты. Начинаешь мыслить компонентами, что-ли.
А если "просто писать" код не задумываясь об этом, то код получается связанным, и становится еще труднее понять, что и как работает.
Здравствуйте, petroa, Вы писали:
p> ·>То добавить dao1 в Svc2 никаких дополнительных правок не потребует. p> Разве, а конструктор? Впрочем, не важно.
Да. Просто добавляешь и introduce parameter, IDE сама всё сделает.
p> ·>Зато в Dao1 добавить svc1 у тебя просто не получится скомпилировать. p> Не получится, да. А при использовании ioc-контейнера может и получиться
Так это плохо, т.к. у тебя возникнут циклические зависимости между слоями приложения.
p> (смотря как конфигурация описана).
По умолчанию у тебя один контекст со всеми зависимостями, глобальая переменная. Разделять контекст на части в контейнерах очень тяжело и то что ошибки стали рантайм — ещё сильнее бьёт.
p> ·>Далее по тексту начинался новый параграф.. я не понял в чём "но". p> Ну, перед этим по тексту ) Я по смыслу имел в виду — в "автоматическом" управлении зависимостями (контейнер) vs "статическом" есть свои плюсы и минусы, и возникновение ошибки в рантайме минус, конечно — но нужно учитывать, что "рантайм" тут не тот, который будет долго ждать своего часа.
В том то и дело, увидеть эти ошибки — нужно ждать часа. В статическом ошибки у тебя тут же подсвечиваются красным в IDE.
p> Однако, при неких удобствах они, за исключением некоторых специфичных применений (описанных в т.ч. VladD2) ухудшают качество дизайна, и это необходимо понимать. Это так, немного банальностей в качестве резюме для себя.
Единственное неоспоримое удобство которое в дискуссии было описано — если код пишешь в нотепаде, то нужно нажимать меньше кнопок.
Поэтому я считаю контейнеры пережитком прошлого, когда код писать было сложно.
Здравствуйте, Буравчик, Вы писали:
Б>Рискую быть непонятым, но скажу, что DI это даже круче — в некотором роде это ФП-стиль построения большого приложения. Как в ФП мы передаем замыкание ("настроенную" функцию), так в DI мы передаем зависимость (уже "настроенный" сервис).
Так за все приходится платить. Разбираться в коде созданном на базе большинства DI действительно сложнее. По уму нужно делать статический DI, который резолвил бы зависимости во время компиляции. Это сняло бы много проблем. Но современные мэйстрим-языки на это не особо рассчитаны. Вот и появляются строковые конфиги. Ошибки при загрузке. Непонимание того почему тут подсунули некоторый тип, а не иной. И т.п.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
·>Да. Просто добавляешь и introduce parameter, IDE сама всё сделает.
Ну то такое, оттуда потом ноги у проверок на null и прочего растет.
·>Так это плохо, т.к. у тебя возникнут циклические зависимости между слоями приложения.
Именно, поэтому об этом и написал. Нужно учитывать такого рода вещи при проектировании конфигурации, дизайна. Как и обычно, впрочем.
·>По умолчанию у тебя один контекст со всеми зависимостями, глобальая переменная. Разделять контекст на части в контейнерах очень тяжело и то что ошибки стали рантайм — ещё сильнее бьёт.
Что именно подразумевается под "разделять контекст на части в контейнерах"?
·>В том то и дело, увидеть эти ошибки — нужно ждать часа. В статическом ошибки у тебя тут же подсвечиваются красным в IDE.
При первом запуске, да. То, что "подсвечиваются красным" — получше, конечно.
·>Единственное неоспоримое удобство которое в дискуссии было описано — если код пишешь в нотепаде, то нужно нажимать меньше кнопок. ·>Поэтому я считаю контейнеры пережитком прошлого, когда код писать было сложно.
Отчасти понимаю позицию, раз так, то пусть будет для вас так.
Здравствуйте, VladD2, Вы писали:
VD> Б>Рискую быть непонятым, но скажу, что DI это даже круче — в некотором роде это ФП-стиль построения большого приложения. Как в ФП мы передаем замыкание ("настроенную" функцию), так в DI мы передаем зависимость (уже "настроенный" сервис). VD> Так за все приходится платить. Разбираться в коде созданном на базе большинства DI действительно сложнее. По уму нужно делать статический DI, который резолвил бы зависимости во время компиляции. Это сняло бы много проблем. Но современные мэйстрим-языки на это не особо рассчитаны. Вот и появляются строковые конфиги. Ошибки при загрузке. Непонимание того почему тут подсунули некоторый тип, а не иной. И т.п.
В Скале есть неявные параметры которые это вроде как позволяют. Но по-моему это даже хуже. На демках это круто, но чуть дальше в лес и пришли...
Мне кажется, что зависимости это важная часть дизайна и к ней нужно подходить осторожно, описывать явно. Нахаляву получается отличная compile-checked документация для кода. А все эти контейнеры и неявности — экономия на строчках кода (лишь за счёт того, что мы называем конфиги не кодом!) без каких-либо улучшений качества.
p> ·>По умолчанию у тебя один контекст со всеми зависимостями, глобальая переменная. Разделять контекст на части в контейнерах очень тяжело и то что ошибки стали рантайм — ещё сильнее бьёт.
p> смотря как конфигурация описана
Видимо я не понял это. А как какие варианты ты имел в иду?
p> Что именно подразумевается под "разделять контекст на части в контейнерах"? https://www.baeldung.com/spring-boot-context-hierarchy https://www.blackpepper.co.uk/blog/a-modular-architecture-with-spring-boot
По-моему это просто ужас. Вместо пары классов для модулей тут какие-то шаманства с аннотациями и хитрыми api.
p> ·>В том то и дело, увидеть эти ошибки — нужно ждать часа. В статическом ошибки у тебя тут же подсвечиваются красным в IDE. p> При первом запуске, да. То, что "подсвечиваются красным" — получше, конечно.
Нередко бывает, что первый запуск который выявит эти ошибки случается только после коммит-пуш-билд-деплой цикла и "час" тут не фигура речи.
Б>Абстракции позволяют создавать нам сложные системы. Через абстракции не надо продираться, их надо принять (понять), не залезая внутрь. На то они и абстракции.
В неком идеальном мире — да, и то при одном важном условии: общее количество абстракций, с которыми приходится жонглировать, не превышает некоего числа N. Которое, кстати, совсем невелико.
Но мы живем в неидеальном мире.
1. Создаваемые на скорую руку абстракции чаще всего leaky.
2. Конкретные реализации абстракций содержат ошибки.
3. Поиск ошибок в большом количестве компонентов — сложная задача.
4. Протестировать компонент обычно сложнее, чем функцию.
5. В большинстве случаев при наличии значительного числа компонентов связность системы начинает расти. И почти всегда приходит к схеме "все зависит от всего", оно же копромонолит.
Б>Про DI добавлю, что когда используешь DI тебе приходится разделять код на независимые компоненты. Начинаешь мыслить компонентами, что-ли. Б>А если "просто писать" код не задумываясь об этом, то код получается связанным, и становится еще труднее понять, что и как работает.
Связность всегда связность. Перенося связность на более высокий уровень (от связности на уровне функций к связности на уровне компонентов) может возникать неоправданое усложнение. К примеру, мне нужно всего лишь сконвертировать дату/время. Но чтобы это сделать, приходится затаскивать огромный "компонент", который чуть ли не через HTTP календарь выставляет. А, еще пример из жизни, — openssl не поддерживает определенный вариант BLAKE2. Поэтому разработчик, ничтоже сумняшеся, впупыживает на все запущеные в компании микросервисы еще и NaCl (который искомый вариант хеш-функции поддерживает).
·> (лишь за счёт того, что мы называем конфиги не кодом!) без каких-либо улучшений качества.
Ага, любимый трюк плохих программистов.
Тестировать лень?
Давайте добавим "конфиг" — переменную "фича включена".
И сделаем slow rollout. Если юзеры не жалуются, значит, все хорошо, пожалуются — выключим, исправим, снова slow rollout.
Добавим так с десяток фич, и выкатывать следующую — жуть и ужас, потому что что бы ты ни тронул, где-то сломается, а протестировать можно только в продакшн.
Здравствуйте, SkyDance, Вы писали:
AA>>Лично мое мнение, выучи все подходы, а потом забудь все и пиши сообразуясь с ситуацией.
SD>Выше по теме уже выразили мнение, с которым я особенно согласен: лучший код — тот, который сразу понятно что делает. А это, в свою очередь, такой код, где минимум уровней абстракции, через которых нужно продраться.
Согласен, меньше кода — проще код.
Простая логика.
Но все равно существуют разные мнения.
Например, Рич Хикки и Роберт Мартин. Первый за простоту, второй за архитектуру со старта, а это значит DI и прочие прелести, когда они еще не особо вроде и нужны.
Здравствуйте, VladD2, Вы писали:
VD>У ТС DI не используется по причини не любви к ним. А все остальное попытка подобрать аргументы к исходной установке.
ТС пришел в проект,где DI\IoC во весь рост и не понимает кому и зачем оно нужно. Ибо столкнулся с трудностями при работе --
удалил якобы не нужный класс (нигде не использовался), а он использовался, только сильно не явно.
VD>В-третьих, люди использующие DI должны понимать что происходит и производить поиск другими методами. В нашем случае, например, ищутся вызовы "...Register<НужныйТип>(...". Сам DI не знает что подсунуть вместо интерфейса. Стало быть без регистрации не обойтись. Ну, а если это конкретный sealed тип и в регистрации он не замечен, то он тупо создается через DI.
Суть в том, что искать надо вовсе не в исходниках, что поначалу может сильно удивлять.
Здравствуйте, SkyDance, Вы писали:
AA>>по мне так это философия. как только нужно расширить поведение объекта, так сразу возникает необходимость интерфейса и следовательно фабрики.
SD>Не вижу связи. Фабрика — это лишь способ добавить turing-full код туда, где изначально его не было. А DI — это способ вернуть с уровня turing full на DSL (см. configuration complexity clock). И то, и другое — в runtime, то есть поздно и не особо безопасно.
Как-то продивинуто . Зачем тут полноту по Тьюрингу поминать? Вся суть в том, что DI\IoC не предполагает
явно создания объектов программистам, но иногда это необходимо. Для этих целей фабрику и выдумали и инжектят
ее в конструкторах, чтобы иметь больше контроля над созданием объектов. Как я это понимаю. Т.е. new вызывать нельзя,
но когда сильно надо используют фабрику.