а нужен ли dependency injection ?..
От: MadHuman Россия  
Дата: 29.08.20 15:42
Оценка: :)
Все привет!

В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода

EmailSender.SendMail(...)

делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...

но зачем?
для тестов? на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме
просто ничего реально не посылает, ну или как-то иначе нужным образом изменяет поведение для тестов.
1-й вариант и проще (нет кучи лишних сущностей, заморачивания с их моками/инстанцированием и тп), и потребности тестирования покрывает..
Re: а нужен ли dependency injection ?..
От: rosencrantz США  
Дата: 29.08.20 16:07
Оценка:
Здравствуйте, MadHuman, Вы писали:

MH>Все привет!


MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...

MH>но зачем?
MH>для тестов?

Реализация IEmailSender в коде может быть и одна, но в тестах этот же самый интерфейс переиспользуется для моков. Мой последний дотнет был уже давно, но вроде необходимость иметь интерфейс обусловлена тем, что в классах методы по умолчанию не виртуальные и, соответственно, нельзя сделать мок, взяв от класса только интерфейс. В Java можно, там этой традиции ISomething/SomethingImpl нет.

MH>на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме


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

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

MH>1-й вариант и проще (нет кучи лишних сущностей, заморачивания с их моками/инстанцированием и тп), и потребности тестирования покрывает..

Ну они не лишние сущности. Если вы вообще их воспринимаете как нечто отдельное, то оформлять их отдельными сущностями в коде — совершенно логично.
Re: а нужен ли dependency injection ?..
От: bnk СССР http://unmanagedvisio.com/
Дата: 29.08.20 16:08
Оценка:
Здравствуйте, MadHuman, Вы писали:

MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...


MH>но зачем?

MH>для тестов?

Да

MH>на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме

MH>просто ничего реально не посылает, ну или как-то иначе нужным образом изменяет поведение для тестов.
MH>1-й вариант и проще (нет кучи лишних сущностей, заморачивания с их моками/инстанцированием и тп), и потребности тестирования покрывает..

Если объект знает, что ты его тестируешь, то он перестает интерферировать может вести себя по-другому (см. гейзенбаг например)
Отредактировано 29.08.2020 16:15 bnk . Предыдущая версия .
Re: а нужен ли dependency injection ?..
От: Qulac Россия  
Дата: 29.08.20 16:47
Оценка: +1
Здравствуйте, MadHuman, Вы писали:

MH>Все привет!


MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...


MH>но зачем?

MH>для тестов? на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме
MH>просто ничего реально не посылает, ну или как-то иначе нужным образом изменяет поведение для тестов.
MH>1-й вариант и проще (нет кучи лишних сущностей, заморачивания с их моками/инстанцированием и тп), и потребности тестирования покрывает..

Работу по внедрению зависимостей лучше доверить какому ни будь одному объекту(ioc-контейнер), а выделение интерфейса позволяет нам легко заменять его реализацию для получения нужного тестового поведения.
Программа – это мысли спрессованные в код
Re: а нужен ли dependency injection ?..
От: Mr.Delphist  
Дата: 29.08.20 20:12
Оценка:
Здравствуйте, MadHuman, Вы писали:

MH>Все привет!


MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


Это ж синглтон, по сути.

MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...


Или перепоручить это именно DI-фреймворку (Ninject, AutoFac и т.п.)
Re: а нужен ли dependency injection ?..
От: Doc Россия http://andrey.moveax.ru
Дата: 30.08.20 04:27
Оценка: 2 (1)
Здравствуйте, MadHuman, Вы писали:

MH>но зачем?

MH>для тестов? на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме

Ок, берем этот пример с EmailSender и смотрим дальше. Пусть использует некий OrderService для отправки email покупателю.
Нужно написать тесты для OrderService в случаях
— письмо не отправлено из-за неправильных данных
— письмо успешно отправлено
— письмо не отправлено из-за ошибки сети
— письмо не отправлено из-за реально не существующего email адреса
Последние 2 пункта добавляет в код EmailSender еще флаги для включения эмуляции ошибок + код для их эмуляции. И все это
— в одном и том же классе (смешиваем обязанности, кода становится больше, он менее читаемый).
— уедет в прод, где есть хоть небольшой, но шанс, оставить флаги включенными
.
В случае с интерфейсами не нужно флагов и кода. Просто надо используя тот же Moq создать mock интерфейса, который будет кидать нужное исключение или возвращать нужное значение. Что выглядит лаконичнее, лежит отдельно от кода сервиса и понимать проще (когда логика сервиса не смешана с тестами).

А теперь еще представьте что EmailSender не ваш, а из сторонний библиотеки? Ну конечно можно прикрутить декоратор поверх, но как это будет выглядеть?
Re: а нужен ли dependency injection ?..
От: Ночной Смотрящий Россия  
Дата: 30.08.20 08:49
Оценка:
Здравствуйте, MadHuman, Вы писали:

MH>но зачем?


Модно.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: а нужен ли dependency injection ?..
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 01.09.20 21:12
Оценка: 5 (2) +1
Здравствуйте, MadHuman, Вы писали:

MH>для тестов?


Нет. Тесты можно и без интерфейсов писать.
Главная прелесть Dependency Injection — в управлении зависимостями. Просто втыкаем IEmailSender там, где мы хотим его использовать и не беспокоимся о том, как его туда передать. DI-фреймворк всё сделает для нас. Нужно какое-то гибкое управление, вот эту вот зависимость создавать каждый раз заново, а вот ту использовать в духе синглтона? Фреймворк всё сделает, укажем конфигурацию в одном месте, не нужно будет бегать по коду и менять передачу зависимости на создание новых объектов.

Отдельно, появляется возможность прозрачно обернуть создаваемые объекты, если нам нужно прозрачное логирование вызовов, прозрачное логирование исключений, или ещё что-то в этом роде. Если вы хотите чтобы все операции в слое работы с базой данных подсчитывали метрики производительности, подсчёт количества вызовов и т.д. — добавляете нужный перехватчик, указываете, что вставлять его нужно только в объекты из неймспейса com.something.myproject.db и всё. Удобно.

Удобство написания тестов в таком сценарии — приятное последствие, а не самоцель.
С уважением, Artem Korneev.
Re: а нужен ли dependency injection ?..
От: Vladek Россия Github
Дата: 04.09.20 10:58
Оценка:
Здравствуйте, MadHuman, Вы писали:

MH>Все привет!


MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...


MH>но зачем?

MH>для тестов? на моем опыте, можно делать где-то глобальный флаг типа TestMode, и сервис типа EmailSender в тестовом режиме
MH>просто ничего реально не посылает, ну или как-то иначе нужным образом изменяет поведение для тестов.
MH>1-й вариант и проще (нет кучи лишних сущностей, заморачивания с их моками/инстанцированием и тп), и потребности тестирования покрывает..

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

https://www.amazon.com/Object-Oriented-Analysis-Design-Applications-3rd/dp/020189551X/
https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/
https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201/
https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599/
https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/
Re[2]: John Ousterhout
От: Sharov Россия  
Дата: 04.09.20 12:56
Оценка:
Здравствуйте, Vladek, Вы писали:

V>https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201/


Есть где-то pdf этой книги, а то я не нашел? Или бумагу читали?
Кодом людям нужно помогать!
Re[3]: John Ousterhout
От: Vladek Россия Github
Дата: 04.09.20 13:34
Оценка:
Здравствуйте, Sharov, Вы писали:

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


V>>https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201/


S>Есть где-то pdf этой книги, а то я не нашел? Или бумагу читали?


Да всё есть в сети.
Re[4]: John Ousterhout
От: Sharov Россия  
Дата: 04.09.20 14:05
Оценка:
Здравствуйте, Vladek, Вы писали:

S>>Есть где-то pdf этой книги, а то я не нашел? Или бумагу читали?

V>Да всё есть в сети.

Ссылкой не поделитесь, так чтобы регистрироваться не надо было? А то первые 2 страницы в гугле наглухо спамом забиты.
Кодом людям нужно помогать!
Re[5]: John Ousterhout
От: Vladek Россия Github
Дата: 05.09.20 08:32
Оценка: 8 (1)
Здравствуйте, Sharov, Вы писали:

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


S>>>Есть где-то pdf этой книги, а то я не нашел? Или бумагу читали?

V>>Да всё есть в сети.

S>Ссылкой не поделитесь, так чтобы регистрироваться не надо было? А то первые 2 страницы в гугле наглухо спамом забиты.


Не помню, можно ли тут выкладывать прямые ссылки на пиратщину. Слово "либген" должно нагуглить адрес сайта, где всё есть.
Re[2]: а нужен ли dependency injection ?..
От: Codealot Земля  
Дата: 08.09.20 23:06
Оценка: +1
Здравствуйте, bnk, Вы писали:

bnk>Если объект знает, что ты его тестируешь, то он перестает интерферировать может вести себя по-другому (см. гейзенбаг например)


То же самое касается моков.
Ад пуст, все бесы здесь.
Re[2]: а нужен ли dependency injection ?..
От: rosencrantz США  
Дата: 09.09.20 03:30
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Здравствуйте, MadHuman, Вы писали:


MH>>но зачем?


НС>Модно.


Модно — простота, когда весь код в одну кучу — один метод на 30 строчек проще, чем 5 классов по 6 строчек + IoC-контейнер. Потому что YAGNI.
Re: а нужен ли dependency injection ?..
От: varenikAA  
Дата: 09.09.20 04:03
Оценка: -1
Здравствуйте, MadHuman, Вы писали:

Однозначно нужны. Как только ты начинаешь писать реальное ПО, то сразу понимаешь всю прелесть.
Это не просто красивое название. Это практически готовый фундамент для архитектуры.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: а нужен ли dependency injection ?..
От: Doc Россия http://andrey.moveax.ru
Дата: 09.09.20 05:28
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Модно — простота, когда весь код в одну кучу — один метод на 30 строчек проще, чем 5 классов по 6 строчек + IoC-контейнер. Потому что YAGNI.


Ну да, а еще для Hello World не нужны любые архитектуры, облака, шины данных итд Да большая часть возможностей C# тоже.
Вот только в реальности разработчики пишут приложения гораздо сложнее 30 сторк.
Re[3]: а нужен ли dependency injection ?..
От: Ночной Смотрящий Россия  
Дата: 09.09.20 07:40
Оценка: 4 (1) +1
Здравствуйте, rosencrantz, Вы писали:

R>Модно — простота, когда весь код в одну кучу — один метод на 30 строчек проще, чем 5 классов по 6 строчек + IoC-контейнер. Потому что YAGNI.


Ну когда у тебя только один метод на 30 строчек то может быть. А когда какой нибудь ASPNET Core у которого только искаропки сотни, если не тысячи, extension points, то жирнота полностью статических контрактов для этого будет запредельной, а расширяемость околонулевой.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: а нужен ли dependency injection ?..
От: Ночной Смотрящий Россия  
Дата: 09.09.20 07:58
Оценка:
Здравствуйте, varenikAA, Вы писали:

AA>Однозначно нужны. Как только ты начинаешь писать реальное ПО, то сразу понимаешь всю прелесть.


И в чем она?

AA>Это не просто красивое название. Это практически готовый фундамент для архитектуры.


Фиговой в основном.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: а нужен ли dependency injection ?..
От: diez_p  
Дата: 09.09.20 23:54
Оценка: 70 (1) +3
Здравствуйте, MadHuman, Вы писали:

MH>Все привет!


MH>В .Net часто принято в проектах пихать dependency injection, например вместо статического класса с вызовом метода


MH>EmailSender.SendMail(...)


MH>делают интерфейс IEmailSender, класс с реализацией EmailSenderImpl (как правило он один), везде где надо заморачиваются чтоб инстанциировать класс...


Это TDD головного мозга.
1) засоряет контракт, там где можно жестко воткнуть тип, будет воткнут интерфейс. Если у типа есть статические методы и инстансовые методы, то смешения обращения то по типу то по интерфейсу начинает вырывать глаз.
2) тройное дублирование сущности, 1 интерфейс, 1 реализация — и непонятный контракт, то ли мне для интерфейса свою реализацию писать, то ли использовать текущую. тот же запечатанный MailSender — не даает вариантов для разгула.


Интерфейс нужен
1) него несколько реальных реализаций,
2) По какой-то причине реализацию хотят скрыть от клиента, например интерфейс public, а Impl internal, вполне допустимо когда контракт модуля публичный, а реализация пакетная

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

DI тут вообще не при чем, DI позволяет делегировать создание объекта контейнеру, но в общем никто не запрещает сделать
di.Register<MailTransport>()
di.Resolve<MailClinet>, который принимает MailTransport в конструкторе.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.