Re[11]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 00:01
Оценка:
S>1. В 70% случаев нужные нам данные уже есть <...>, просто кто-то поленился

"Поленился" есть причина как бы не 99% проблем, в любой области, не только программировании
Re[15]: О пользе Dependency Injection
От: Somescout  
Дата: 16.01.21 00:05
Оценка:
Здравствуйте, ·, Вы писали:

S>> В смысле? Взял из конструктора:

·>Этот антипаттерн называется Service Locator... Противоположность DI.
Я написал ниже в каких случаях я его использую. От того что что-то называют "антипаттерном" оно не становится автоматически плохим.

S>> Если вам интересен вариант когда провайдер создаётся вручную, то примерно так (псевдокод):

·>Т.е. кода таки больше получилось.
Инициализация DI делается один раз. "Так что кода всё-таки меньше получилось" (и да, "аргумент секретарши" судя по всему идёт через года).

S>> Понятно что этот способ (т.е. через ServiceProvider) используется исключительно в том случае, когда инициализация SomeObject дорогая и сам объект используется он не всегда (я использую его только в контроллере, когда лишь часть экшенов использует его),

·>Вроде это банальный Lazy...
И?

S>> Ну да, сравниете с ручным созданием ServiceProvider — разница в несколько строк. И да, у меня почему-то ни разу не возникало ситуации, когда проблемы с DI были с "рантайм-ошибок и километровых стек-трейсов" — обычно просто сообщение, что требуемый объект не зарегистрирован.

·>А если не использовать фреймворк, то такого рода ошибки будут сразу в IDE подсвечиваться, как ошибки компиляции.
Только в том случае, если вы прямо или косвенно завязаны на конкретные классы. Что, внезапно, тоже "антипаттерн".

S>> И да, этот "весь код" явно ссылается на **реализации** используемых классов, затрудняя его переиспользование (за которое тут так ратуют),

·>Такой код собирает конечное приложение, так называемый Composition Root. Если части приложения нужно переиспользовать, классы можно интегрировать в модуль с подходящими областями видимости и зависимостями, а не кидать всё в глобальный мусорный контейнер.
Когда я вижу как кто-то ударяется в демагогию, у меня возникает стойкое ощущение отсутсвия у него аргументов.

S>> более того, если инициализация чуть сложнее (например нужно получить ConnectionString) — этот код ляжет не один раз в настройку DI, а во все его вызовы. Про всякие мелочи, вроде использование пулов или скоупы даже говорить бессмысленно.

·>Нет, тот же ConnectionString прокинется через ровно такой же DI.
Зачем? Вы просто зададите в DI логику получения зависимости и всё, больше её нигде повторять не нужно.
ARI ARI ARI... Arrivederci!
Re[16]: О пользе Dependency Injection
От: · Великобритания  
Дата: 16.01.21 00:36
Оценка:
Здравствуйте, Somescout, Вы писали:

S> ·>Этот антипаттерн называется Service Locator... Противоположность DI.

S> Я написал ниже в каких случаях я его использую. От того что что-то называют "антипаттерном" оно не становится автоматически плохим.
И в этих случаях он таки не нужен, внезапно. Т.е. антипаттерн. Вот смотришь на такое CallerObj(IServiceProvider serviceProvider) и пока не просмотришь весь код такого класса — никак не не поймёшь от чего собственно он зависит. Такой класс зависит от _всего_. Глобальные Переменные™, просто под другим соусом.

S> ·>Т.е. кода таки больше получилось.

S> Инициализация DI делается один раз. "Так что кода всё-таки меньше получилось" (и да, "аргумент секретарши" судя по всему идёт через года).
Composition Root тоже делается один раз.

S> S>> Понятно что этот способ (т.е. через ServiceProvider) используется исключительно в том случае, когда инициализация SomeObject дорогая и сам объект используется он не всегда (я использую его только в контроллере, когда лишь часть экшенов использует его),

S> ·>Вроде это банальный Lazy...
S> И?
И то, что достаточно иметь Lazy<SomeObject> из которого можно получить ровно то что надо, а не что угодно и хз что подразумевалось.

S> ·>А если не использовать фреймворк, то такого рода ошибки будут сразу в IDE подсвечиваться, как ошибки компиляции.

S> Только в том случае, если вы прямо или косвенно завязаны на конкретные классы. Что, внезапно, тоже "антипаттерн".
Ты так говоришь как будто ...Bind<IDbContext,DbContext>... не завязано на конкретные классы. Суть Composition Root это и есть связывание конкретных классов.

S> S>> И да, этот "весь код" явно ссылается на **реализации** используемых классов, затрудняя его переиспользование (за которое тут так ратуют),

S> ·>Такой код собирает конечное приложение, так называемый Composition Root. Если части приложения нужно переиспользовать, классы можно интегрировать в модуль с подходящими областями видимости и зависимостями, а не кидать всё в глобальный мусорный контейнер.
S> Когда я вижу как кто-то ударяется в демагогию, у меня возникает стойкое ощущение отсутсвия у него аргументов.
В чём демагогия? Контейнер это глобальная свалка всех объектов приложения, из которой беcконтрольно тянется всё отовсюду.

S> ·>Нет, тот же ConnectionString прокинется через ровно такой же DI.

S> Зачем? Вы просто зададите в DI логику получения зависимости и всё, больше её нигде повторять не нужно.
Да, не нужно. Что ты имеешь в виду может повторяться и зачем?
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: О пользе Dependency Injection
От: Министр Промышленности СССР  
Дата: 16.01.21 01:33
Оценка:
IT>- этот код может находится в трудно доступном месте и для его выполнения потребуется создание определённого сценария, подготовки окружения и набора данных.
IT>- проблема может возникать только в определённом окружении недоступном для отладки, а при создании искуственных условий проблема не воспроизводится.

кстати да
и это дефакто означает всю ту же связность компонентов в таких случаях!
Re[11]: О пользе Dependency Injection
От: Министр Промышленности СССР  
Дата: 16.01.21 01:39
Оценка:
AA>>Взять теже фабрики, билдеры они тоже зло?
IT>Да.

не ну это преребор
иногда-иногда и фабрика классическая заходит и билдер
Re[12]: О пользе Dependency Injection
От: Министр Промышленности СССР  
Дата: 16.01.21 01:46
Оценка:
IT>>Т.е. чтобы перенести код пришлось его не слабо так переписать. Правильно?

S>Нет, не правильно: код остался полностью без изменений. Только вместо вызова вида:

S>
var instance = serviceProvider.Get<SomeObject>()

S>Появилось
S>
S>var db = new DbContext();
S>var config = new AppConfig();
S>var instance = new SomeObject(db, config);
S>


я такое пишу, если умещается в экран, в одну строчку:

var instance = new SomeObject(new DbContext(), new AppConfig());
Re[7]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 02:36
Оценка: 106 (4)
S>А какой был старый термин?

Конструктор. Если мы не передаем что-то explicitly, мы просто передаем это что-то implicitly, сбоку и невидимым путем. Через реестр в DI container, например.

Простыми словами: у нас есть реестр (глобальных в пределах одного DI container) статических переменных. Например, в XML-файле, bean definitions.


  Скрытый текст
Можно написать так:
logger = new MyLoggerTypeInstance().

Можно так:
logger = MyLoggerFactory::createInstance().

А можно сделать вид, что мы никогда не конструируем объект императивно, и как будто бы некий DI container будет конструировать объект за нас. Но реальность такова, что кто-то же должен сконструировать сам di container, и заодно задать все зависимости между объектами в контейнере.
И в развернутом виде будет создание объекта будет выглядеть так:

logger = new Logger(someGlobalDIContainer::readConfigFor(Logger)).

Что в итоге? Ровно те же зависимости. Только теперь записанные в "конфигурации контейнера". На другом языке (XML, например). Превосходная иллюстрация на тему "configuration complexity clock"

И XML это всего лишь третья стадия. А когда мы начинаем вместо XML писать "генераторы XML" (уверен, что кто сталкивался со "зрелыми" наслоениями мамонтов, обязательно на что-то такое натыкался), — вот это веселуха. Писать Java-код, который читает XML, который сгенерирован кодом на, скажем, Python. Получили сложность на ровном месте.
Re[10]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 02:44
Оценка: 1 (1) +1
AA>Взять теже фабрики, билдеры они тоже зло?

Примененные где не надо — конечно, зло.
"Фабрики" это лишь способ добавить уровень абстракции к оператору создания объекта.

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

Но в реальности эти переходники оказываются не нужны почти никогда. Зато они добавляют лишних контактов и соединений, делают розетку менее надежной, а потом и вовсе приводят к пожару, потому что не были рассчитаны на ток, который там случайно оказался.
Re[7]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 02:49
Оценка:
IT>Можно пример тестирования конфигурации Production?

Поясни, что ты имеешь в виду. Тестируют не конфигурацию, а систему (SUT, system under test).
При внесении изменений в конфиругацию SUT тоже меняется, и нужны тесты не то, что эта измененная система работает как ожидается.

Иными словами, если ты внес изменения "теперь эта кнопка зеленая", надо написать соответствующий тест, который убеждается, что кнопка в натуре зеленая (упрощенно, но суть, думаю, понятна).
Re[8]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 02:50
Оценка:
P>Но в описываемой мною ситуации b не может быть null по определению, в этом случае контейнер бросает вполне читаемое подробное исключение, что такая-то завимисость в таком-то месте не может быть удовлетворена. Снова, возвращаемся к конфигурации и определяем эту зависимость — всё "в рамках".

Без DI framework это же исключение будет брошено сильно раньше. Компилятором. Причем еще и подсвечено в IDE.
Re[8]: О пользе Dependency Injection
От: SkyDance Земля  
Дата: 16.01.21 03:03
Оценка: +1
МП>и это дефакто означает всю ту же связность компонентов в таких случаях!


https://ferd.ca/complexity-has-to-live-somewhere.html
Re[11]: О пользе Dependency Injection
От: microuser  
Дата: 16.01.21 08:40
Оценка:
Здравствуйте, IT, Вы писали:

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


M>>Так в данном примере вообще ничего искать не нужно, т.к. при использовании DI экземпляр Bar будет создаваться контейнером и все его зависимости будут так же создаваться контейнером, соответственно нужно посмотреть только код конфигурации этого контейнера.


IT>И что я там увижу? Список имён классов?


M>>Кроме того современные контейнеры никогда к такой ситуации не приведут, т.к. Bar не сможет быть равен null, будет ошибка при попытке создания Foo о том что не удалось разрешить зависимость Boo и там же в стек трейсе будет видно почему не удалось.


IT>По-поводу умения абстрагироваться я уже выше отписал.


Я же отвечал на конкретный код, который ты привел.

Хорошо, давай модифицируем пример:

class Foo
{
public Foo(Bar b)
{
if (b.Atata == null)
throw ...
}
}

В данном случае при ошибке у нас будет стек трейс в котором будет прекрасно видна последовательность вызовов, поэтому ходить по конструкторам и изучать кто кого вызывал уже будет не нужно.
Предположим что стек трейса у нас нет, но мы знаем что ошибка была и хотим понять как так получилось что Atata == null. Тогда нам нужно нажать Shift+F12 на свойстве Atata, но не на конструкторе. Вот это реальный кейс который часто встречается на практике.

Просто кейс что при использовании DI в конструктор передали null невозможен в принципе, тут нужен другой пример.
Re[11]: О пользе Dependency Injection
От: microuser  
Дата: 16.01.21 08:44
Оценка: :)
Здравствуйте, IT, Вы писали:

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


M>>Например есть у нас некий полезный класс у которого две зависимости:


IT>
M>>class Service
M>>{
M>>    public Service(IDependency1 dependency1, IDependency2 dependency2)
M>>    {
M>>    }

M>>    public void DoSomeWork()
M>>    {
M>>        //...
M>>    }
M>>}
IT>


IT>Можно я ещё немного добавлю полезности? Спасибо!


IT>
IT>enum SourceType
IT>{
IT>    Foo,
IT>    ar
IT>}

IT>class Service
IT>{
IT>    public Service(IDependency1 dependency1, IDependency2 dependency2, SourceType sourceType)
IT>    {
IT>    }

IT>    public void DoSomeWork()
IT>    {
IT>        //...
IT>    }
IT>}
IT>


IT>Как DI фреймворк разрулит ситуацию для разных sourceType?



Так не пишут при использовании DI. Нужно постараться уйти от зависимости которую нельзя разрешить автоматически. Как вариант SourceType можно попробовать перенести из конструктора в DoSomeWork.
Re[13]: О пользе Dependency Injection
От: petroa  
Дата: 16.01.21 09:45
Оценка: +1
·>Он может приехать из какой-нибудь кастомной фабрики.

Может, но это к вопросу контейнера не относится, ведь он также может приехать и при "ручном" создании.

·>Компилятор тут притом, что вот такие вещи "современные контейнеры никогда к такой ситуации не приведут, т.к. Bar не сможет быть равен null, будет ошибка при попытке создания Foo о том что не удалось разрешить зависимость Boo и там же в стек трейсе будет видно почему не удалось." ·>в коде без контейнера будут тупо ошибками компиляции. А уж эти современные контейнерные стек-трейсы — это какое-то издевательство над здравым смыслом.


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

В "ручном" случае при манипулировании зависимостями (изменениями сигнатур конструкторов) мы постоянно меняем вызывающий код, обкладываемся проверками на null как в примере раньше и прочее. Стоила ли статическая проверка этого? Ну, может, в какой-то степени. Но на мой взгляд это никак не стоппер.

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

А стек-трейсы, они разные бывают, да... в джаве страшноватые, в дотнете я сейчас чаще встречаю вполне вменяемые.
Re[9]: О пользе Dependency Injection
От: petroa  
Дата: 16.01.21 09:49
Оценка:
SD>Без DI framework это же исключение будет брошено сильно раньше. Компилятором. Причем еще и подсвечено в IDE.

Описал там выше мои мысли. На мой взгляд, это можно рассматривать и как плюс контейнера. Современные реалии на платформах типа джавы/дотнета и так много чего оставляют на рантайм из за рефлешена, кроме конфигурации ioc-контейнеров. Они тут еще самые безобидные.
Re[6]: О пользе Dependency Injection
От: barn_czn  
Дата: 16.01.21 10:22
Оценка:
Здравствуйте, varenikAA, Вы писали:

AA>Здравствуйте, Министр Промышленности, Вы писали:


МП>>а реальная необходимость бывает редко и всего в 2-3 местах системы


AA>Очевидно, что DI это реализация принципа инверсии зависимостей.

AA>Если у вас возникают циклические зависимости между компонентами, то вам не избежать DI.


class A
{
    public B RefB;
}

class B
{
    public A RefA;
}


void Main()
{
    var a = new A(){RefB = new B()};
    a.RefB.RefA = a;
}


Циклические зависимости есть. DI в упор не вижу. Ваше слишком общее утверждение опровергнуто.
Re[7]: О пользе Dependency Injection
От: Министр Промышленности СССР  
Дата: 16.01.21 10:44
Оценка:
МП>>>а реальная необходимость бывает редко и всего в 2-3 местах системы

AA>>Очевидно, что DI это реализация принципа инверсии зависимостей.

AA>>Если у вас возникают циклические зависимости между компонентами, то вам не избежать DI.

_>

_>class A
_>{
_>    public B RefB;
_>}

_>class B
_>{
_>    public A RefA;
_>}


_>void Main()
_>{
_>    var a = new A(){RefB = new B()};
_>    a.RefB.RefA = a;
_>}

_>


_>Циклические зависимости есть. DI в упор не вижу. Ваше слишком общее утверждение опровергнуто.


да не, очевидно же что под компонентами имелись в виду сборки
то есть в данном случае A в одной сборке, а B — в другой
и какая на какую ссылается
(я столкнулся с такой проблемой в 2005 году первый раз и решил её самостоятельно, не зная термина DI)
но вопрос обсуждения разумеется не об отказе от DI, а только о радикальном ограничении применения DI-фреймворков
Re[8]: О пользе Dependency Injection
От: barn_czn  
Дата: 16.01.21 11:06
Оценка:
МП>да не, очевидно же что под компонентами имелись в виду сборки
МП>то есть в данном случае A в одной сборке, а B — в другой

Какая разница? замените тип A и B в Ref полях на интерфейс который известен обеим классам, суть от этого не измениться.
Нам впаривают DI и DI фреймворки как нечто сокральное без чего ну никак невозможно писать хороший код.
Re[14]: О пользе Dependency Injection
От: · Великобритания  
Дата: 16.01.21 11:23
Оценка:
Здравствуйте, petroa, Вы писали:

p> ·>Компилятор тут притом, что вот такие вещи "современные контейнеры никогда к такой ситуации не приведут, т.к. Bar не сможет быть равен null, будет ошибка при попытке создания Foo о том что не удалось разрешить зависимость Boo и там же в стек трейсе будет видно почему не удалось." ·>в коде без контейнера будут тупо ошибками компиляции. А уж эти современные контейнерные стек-трейсы — это какое-то издевательство над здравым смыслом.

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

p> Если не зарегистрирована — приложение упадёт с исключением при первом запуске

Не понял в чём плюс. Ошибки в рантайме — плюс?

p> (контейнер сразу строит граф зависимостей, не ждёт ничего в рантайме).

Когда как. Вон там выше чувак вытаскивает зависимости через serviceLocator.Get<Some>().

p> В "ручном" случае при манипулировании зависимостями (изменениями сигнатур конструкторов) мы постоянно меняем вызывающий код, обкладываемся проверками на null как в примере раньше и прочее. Стоила ли статическая проверка этого? Ну, может, в какой-то степени. Но на мой взгляд это никак не стоппер.

Что за вызывающий код? Этот вызывающий код и есть composition root, т.е. описание структуры зависимостей твоего приложения. Отличая дока, кстати.

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

Да, по сути это целый DSL, притом динамический, и со стандартами плохо.
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[17]: О пользе Dependency Injection
От: Somescout  
Дата: 16.01.21 11:34
Оценка:
Здравствуйте, ·, Вы писали:

·>В чём демагогия? Контейнер это глобальная свалка всех объектов приложения, из которой беcконтрольно тянется всё отовсюду.

Как угодно. Если вы уже развесили ярлыки — не вижу смысла в дискуссии.
ARI ARI ARI... Arrivederci!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.