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


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

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

ТСВ>Статья:

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



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

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

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


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

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

P.S. Кстати, в "Смягчаем проблему" не упомянут самый правильный способ — навешивание на ServceLocator extension-методов, которые явно документируют поддерживаемые методы через интеллисенс.
Re[3]: Паттерны внедрения зависимостей
От: Sinix  
Дата: 25.04.16 05:49
Оценка: +1
Здравствуйте, SergeyT., Вы писали:

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

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

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

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