Паттерны внедрения зависимостей
От: Тепляков Сергей Владимирович США http://sergeyteplyakov.blogspot.com/
Дата: 01.04.16 14:34
Оценка: 40 (2) +1 -1
Статья:
Паттерны внедрения зависимостей
Автор(ы): Тепляков Сергей Владимирович
Дата: 23.10.2015
Статья рассказывает о наиболее популярных паттернах внедрения зависимостей, которые будут полезны всем разработчикам, независимо от того, используют они какой-либо контейнер или предпочитают ручную композицию объектов в приложении.


Авторы:
Тепляков Сергей Владимирович

Аннотация:
Статья рассказывает о наиболее популярных паттернах внедрения зависимостей, которые будут полезны всем разработчикам, независимо от того, используют они какой-либо контейнер или предпочитают ручную композицию объектов в приложении.
Re: Паттерны внедрения зависимостей
От: Sinix  
Дата: 01.04.16 16:23
Оценка: 1 (1)
Здравствуйте, Тепляков Сергей Владимирович, Вы писали:

ТСВ>Статья:

ТСВ>Паттерны внедрения зависимостей
Автор(ы): Тепляков Сергей Владимирович
Дата: 23.10.2015
Статья рассказывает о наиболее популярных паттернах внедрения зависимостей, которые будут полезны всем разработчикам, независимо от того, используют они какой-либо контейнер или предпочитают ручную композицию объектов в приложении.



Такой вопрос по ServiceLocator. Почему по-твоему проблема

Самое страшное в Service Locator то, что он дает видимость хорошего дизайна. У нас никто не знает о конкретных классах, все завязаны на интерфейсы, все «нормально» тестируется и «расширяется». Но когда вы попробуете использовать ваш код в другом контексте, или когда кто-то попробует использовать его повторно, вы с ужасом поймете, что у вас есть дикая «логическая» связанность, о которой вы и не подозревали.

относится только к нему и не относится к прочим паттернам?


По-моему, эта проблема общая для любой _динамической_ системы разруливания зависимостей и нюансы конкретной реализации тут мало что решают. Особенно с типовым для биз-логики требованием "настраиваемый объект может _динамически_ создавать другие настраиваемые через DI объекты".

Самое парадоксальное, что я видел — когда локатору прямо противопоставляют IOC фреймворки, хотя у них под капотом по факту всё тот же словарик {тип, метод для получения экземпляра}

P.S. Кстати, в "Смягчаем проблему" не упомянут самый правильный способ — навешивание на ServceLocator extension-методов, которые явно документируют поддерживаемые методы через интеллисенс.
Re: Паттерны внедрения зависимостей
От: Shmj Ниоткуда  
Дата: 01.04.16 21:59
Оценка:
Здравствуйте, Тепляков Сергей Владимирович, Вы писали:

Не понял почему старые статьи подняли? Уже ж читали это...
Re: Паттерны внедрения зависимостей
От: Andir Россия
Дата: 02.04.16 20:11
Оценка:
Здравствуйте, Тепляков Сергей Владимирович, Вы писали:

ТСВ>Аннотация:

ТСВ>Статья рассказывает о наиболее популярных паттернах внедрения зависимостей, которые будут полезны всем разработчикам, независимо от того, используют они какой-либо контейнер или предпочитают ручную композицию объектов в приложении.

В статье на месте картинок *.emz файлы, которые в браузере посмотреть нет никакой возможности

--
C Увадением, Andir!
using( RSDN@Home 1.2.0 alpha 5 rev. 74) { /* Работаем... */ }
Re[2]: Паттерны внедрения зависимостей
От: ionoy Эстония www.ammyui.com
Дата: 05.04.16 09:01
Оценка: 5 (1)
Здравствуйте, Sinix, Вы писали:

S>По-моему, эта проблема общая для любой _динамической_ системы разруливания зависимостей и нюансы конкретной реализации тут мало что решают. Особенно с типовым для биз-логики требованием "настраиваемый объект может _динамически_ создавать другие настраиваемые через DI объекты".


Лично для меня разница, всё-таки, есть. Вот два примера:


//1
public A(ServiceLocator locator) {
  var repository = locator.GetService<IRepository>();
}

//2
public B(IRepository repository) {
}


Казалось бы, действие одно и то же. Но тестировать второй вариант значительно проще, так как у нас есть контракт которому нужно соответствовать.
Если же у нас по коду класса раскаданы вызовы GetService, то это неизменно приводит к тому что тесты сыпятся в рантайме, а это не есть хорошо.

Для себя обозначил такое правило: "ServiceLocator не должен встречаться в тестируемом коде".
www.livexaml.com
www.ammyui.com
www.nemerleweb.com
Отредактировано 05.04.2016 9:02 ionoy . Предыдущая версия .
Re[3]: Паттерны внедрения зависимостей
От: Sinix  
Дата: 05.04.16 10:13
Оценка: 1 (1) +1
Здравствуйте, ionoy, Вы писали:


I>Лично для меня разница, всё-таки, есть. Вот два примера:

I>Казалось бы, действие одно и то же. Но тестировать второй вариант значительно проще, так как у нас есть контракт которому нужно соответствовать.

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

Для бизнес-логики идея подыхает из-за комбинаторного взрыва.
Чтоб было понятно о чём речь. Простейший бизнес-сценарий дёргает сервисы расчёта стоимости заказа, учётную политику для конкретного контрагента, определение адресата по кладр и дальше передаёт результат в 5-7 других биз-сценариев, которые выбираются динамически и могут посылать по почте/печатать отчёт/запускать документоооборот и тыды и тыпы.

Даже для такой мелочёвки проще прокинуть один service locator, при необходимости дополняя DI-инъектором внутри классов, что-то типа serviceProvider.InjectAll(this).
В более сложных вещах, когда только краткая сопроводиловка в бумаге за 200 листов перелазит, микроменеджмент на уровне отдельных сервисов нереален в принципе.
На больших масштабах все эти "вы можете отследить каждую зависимость" превращается в "вы _будете_ отслеживать _каждую_ зависимость". Нафиг-нафиг.


I>Если же у нас по коду класса раскаданы вызовы GetService, то это неизменно приводит к тому что тесты сыпятся в рантайме, а это не есть хорошо.

I>Для себя обозначил такое правило: "ServiceLocator не должен встречаться в тестируемом коде".
А такие штуки уже не ловятся классическими юнит-тестами в принципе, только интеграционными. Масштаб не тот слегка. Ну, как танкер в микроскоп проверять.

На практике вся разница в том, что мы не воссоздаём всё окружение для каждого теста в отдельности. Вместо этого поднимается инстанс сервиса с включенными отладочными ассертами и дальше он топится пачкой проверок, записанных в виде стандартных юнит-тестов. Как опция, сами тесты подгружаются в инстанс сервиса и используют внутреннее API сервиса, т.е. тот самый servicelocator.
Re[4]: Паттерны внедрения зависимостей
От: ionoy Эстония www.ammyui.com
Дата: 05.04.16 10:30
Оценка:
Здравствуйте, Sinix, Вы писали:

I>>Если же у нас по коду класса раскаданы вызовы GetService, то это неизменно приводит к тому что тесты сыпятся в рантайме, а это не есть хорошо.

I>>Для себя обозначил такое правило: "ServiceLocator не должен встречаться в тестируемом коде".
S>А такие штуки уже не ловятся классическими юнит-тестами в принципе, только интеграционными. Масштаб не тот слегка. Ну, как танкер в микроскоп проверять.

S>На практике вся разница в том, что мы не воссоздаём всё окружение для каждого теста в отдельности. Вместо этого поднимается инстанс сервиса с включенными отладочными ассертами и дальше он топится пачкой проверок, записанных в виде стандартных юнит-тестов. Как опция, сами тесты подгружаются в инстанс сервиса и используют внутреннее API сервиса, т.е. тот самый servicelocator.


У меня для таких вещей есть специальный конструктор в конфигураторе IOC, куда я могу передавать тестовые сервисы. Тогда поднимается вся инфраструктура, но с выбранными мною сервисами (моками например).
www.livexaml.com
www.ammyui.com
www.nemerleweb.com
Re[5]: Паттерны внедрения зависимостей
От: Sinix  
Дата: 05.04.16 10:53
Оценка:
Здравствуйте, ionoy, Вы писали:

S>>На практике вся разница в том, что мы не воссоздаём всё окружение для каждого теста в отдельности. Вместо этого поднимается инстанс сервиса с включенными отладочными ассертами и дальше он топится пачкой проверок, записанных в виде стандартных юнит-тестов. Как опция, сами тесты подгружаются в инстанс сервиса и используют внутреннее API сервиса, т.е. тот самый servicelocator.


I>У меня для таких вещей есть специальный конструктор в конфигураторе IOC, куда я могу передавать тестовые сервисы. Тогда поднимается вся инфраструктура, но с выбранными мною сервисами (моками например).


Мы о разных вещах тут говорим, если я тебя правильно понял

Во-первых, моки не спасут в ситуации, когда у тебя с десяток сервисов и нужно убедиться, что вся эта кухня работает _вместе_. Что обычно и интересует клиента

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

Ну и в-третьих, IOC-фреймворк начинает только мешать, если тестируемый код начинает создавать объекты, которые тоже надо заполнить из IOC-контейнера (и на них цепочка только начинается). В конечном итоге необходимость протаскивать контейнер по всей цепочке превращает IOC в ещё одну импровизацию на тему serviceLocatior.
Отредактировано 05.04.2016 10:54 Sinix . Предыдущая версия .
Re[2]: Паттерны внедрения зависимостей
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 25.04.16 04:23
Оценка: 41 (2)
Здравствуйте, Sinix, Вы писали:

S>Такой вопрос по ServiceLocator. Почему по-твоему проблема

S>

S>Самое страшное в Service Locator то, что он дает видимость хорошего дизайна. У нас никто не знает о конкретных классах, все завязаны на интерфейсы, все «нормально» тестируется и «расширяется». Но когда вы попробуете использовать ваш код в другом контексте, или когда кто-то попробует использовать его повторно, вы с ужасом поймете, что у вас есть дикая «логическая» связанность, о которой вы и не подозревали.

S>относится только к нему и не относится к прочим паттернам?

S>По-моему, эта проблема общая для любой _динамической_ системы разруливания зависимостей и нюансы конкретной реализации тут мало что решают. Особенно с типовым для биз-логики требованием "настраиваемый объект может _динамически_ создавать другие настраиваемые через DI объекты".


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

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

Я же здесь говорю несколько о другой проблеме.
Допустим, мы протаскиваем зависимости явно через конструкторы. В этом случае, если у класса в конструкторе протаскиваются 4-5 зависимостей, то читателю довольно быстро становится очевидным, что с дизайном что-то не так: как-то добавлять 6-й не хочеся, ведь для этого придется поменять не только этот, но еще и пяток соседних классов.
Если же используется локатор, протащенный через конструктор базового класса, то в любой момент времени, любой класс может получить любую зависимость. Система расширяема до нельзя, но рассуждать о том, какие зависимости у конкретного класса уже невозможно. Для этого нужно чтащтельно изучить его детали реализации.

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

S>Самое парадоксальное, что я видел — когда локатору прямо противопоставляют IOC фреймворки, хотя у них под капотом по факту всё тот же словарик {тип, метод для получения экземпляра}


Мое ИМХО, что локатор — это скорее паттер в котором экземпляр контейнера протаскивается явным образом. Мне сложно сказать, как их можно противопоставлять друг другу...

S>P.S. Кстати, в "Смягчаем проблему" не упомянут самый правильный способ — навешивание на ServceLocator extension-методов, которые явно документируют поддерживаемые методы через интеллисенс.


+1: хороший подход. Но сам никогда им не пользовался.
Re[3]: Паттерны внедрения зависимостей
От: Sinix  
Дата: 25.04.16 05:49
Оценка: +1
Здравствуйте, SergeyT., Вы писали:

ST>Я же здесь говорю несколько о другой проблеме.

ST>Допустим, мы протаскиваем зависимости явно через конструкторы. В этом случае, если у класса в конструкторе протаскиваются 4-5 зависимостей, то читателю довольно быстро становится очевидным, что с дизайном что-то не так: как-то добавлять 6-й не хочеся, ведь для этого придется поменять не только этот, но еще и пяток соседних классов.

Ага, другими словами: локатор провоцирует протаскивать себя, даже если по факту не нужен, и тем самым скрывает косяки, которые без локатора вылезли бы раньше, так?

Вот тут согласен целиком и полностью. Засада в том, чтобы использовать локатор только там, где от него есть толк. Не оттоптавшись самостоятельно на граблях эту разницу фиг почуешь. Проблема, да.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.