Как стать автором
Обновить

Комментарии 15

Всё-таки предпочтительным вариантом для получения экземпляра является конструктор. Использование Service Locator оправдано только там, где без этого совсем не обойтись(Например, в фильтрах, но даже и там возможны варианты).
DI через проперти семантически отличается от DI через конструктор тем, что инъекция вобщем-то может и не произойти, или, например, объект может быть изменен после создания. К тому же, вне IoCC окружения, этот модуль необходимо будет создавать особым образом, помня, какие проперти обязательны к установке, а какие нет. Это частный случай Sequential coupling, что является антипаттерном.

Вы как бы пытаетесь развязать зависимости модулей друг от друга, но в то же время жестко подвязываетесь на сам Service Locator. Теряется реюзабельность. Выходит, что модули без определенного и конкретного Service Locator'a существовать не могут, придется воссоздавать окружение. И именно поэтому использование Service Locator'a тоже является антипаттерном. Но, конечно, есть случаи, где по-другому никак, но не в этих примерах в статье.

По этим причинам предпочтительнее делать инъекцию обязательных зависимостей через конструктор в readonly поля. Либо, в случае инъекций в проперти, обеспечить иммутабельность object reference для зависимости и потокобезопасное выполнение. Но это же ненужные сложности. :)
Довольно сложный материал для 2 урока, не усвоил зачем нужен Service Locator
Суть Service Locator — развязать зависимости модулей.

Зачем это нужно? У нас есть границы модуля. Например примерно так будет выглядеть стандартное MVC приложение:


На контроллер завязано много связей, что делать чтобы проверить его работоспособность? Мы должны запустить как-то код. И тут два варианта: или это будет тестер за компьютером, или мы будем проверять программно.

Чтобы проверять программно, мы должны все окружающие его модули. Т.е. всё окружение мы меняем на объекты-заглушки: HttpContext, IRepository, Mail, SmsProvider и др. Т.е. всё окружение — это не реальные модули, а декорации.

Для чего это надо? Если мы снимаем фильм, легче снимать в павильоне, мы восстанавливаем комнату и актеры дубль за дублем играют роль. Если необходима уличная съемка — мы или восстанавливаем в павильоне часть улицы или выходим на улицу. Иногда декорации могут быть дорогостоящие, как в «Шоу Трумена», чтобы актер мог лучше сыграть. Тестирование объекта (в нашем случае контроллера) — это и есть проигрывание сценария.

Если инициализация (вызов конструктора) будет находится в самом контроллере мы не сможем заменить реальное окружение на декорации не изменяя кода. Для этого мы и используем Service Locator.

Больше его суть раскрывается в уроке E, где мы активно начинаем использовать mock-объекты, переделываем NotifyMail (из урока A) под сервис и используем интегрированное тестирование (где тестируем и реальный код SqlRepository).

у меня такой вопрос: какой контейнер IoC вы сами чаще всего используете и почему?
Судя по этому уроку и уроку Е — всё-таки Ninject. От чего же?
Личные предпочтения.
не сочтите за приставалу, а как изначально к нему пришли? Просто вижу, что владеете и другими реализациями принципа IoC, и погуглив и наткнувшись сравнение нескольких контейнеров в т.ч. по перфомансу, увидел, что Ninject далеко не самый лучший, но, вероятно, наиболее удобный
Изначально пользовался им, но когда дошло дело до статей, рассмотрел и другие IoC-контейнеры. Пользовался еще на чужом проекте Unity, но Ninject просто лучше знаю. По производительности, удобству — не сравнивал.
Добрый день,

Я недавно ознакомился с трудом Seemann Mark под названием Dependency Injection in .NET. Не знаю какой вес имеет данный автор в мире .net но меня удивил тот факт что он прямо таки приравнял Service Locator к анти-паттернам. Признаюсь я использую в моих проектах юнити в купе с паттерном Service Locator но как-то не совсем представляю как обойтись без оного или чем заменить. В своих примерах автор часто создает экземпляр контейнера, для объяснения это подходит но для реал-ворлд приложения не очень. Что скажет автор по данному поводу? Спасибо
Я немного запутался, какой автор — это я, а какой автор — это Seemann Mark.
Я просто расскажу, как я считаю. Если вы пишете приложение, то можно обойтись без DI, изначально. Но потом — ближе к глобальным изменениям проекта наступает кризис. Мы изменяем что-то. Другой программист изменяет что-то. И всё перестает работать. Надо тестирование. Для тестирования необходим DI. В некоторых случаях этот момент может и не произойти. Вы сделали проект, его оплатили (или не оплатили) и всё это DI-использование повисло в воздухе. Оно не сыграло свою положительную роль. Мои проекты доживают до стадии глобальных изменений. DI мне нужен, заказчики платят деньги. Все довольны.
Но (!) возможно Seemann Mark пошел дальше, он участвовал в проекте и в какой-то неясной ситуации достиг критической точки. И сидя дома в думах понял глобальную ошибку — «не надо было использовать Service Locator». И написал по этому поводу книгу.
Я не отношусь к тому типу людей, которые говорят: «Это используйте, это хорошо. Это не используйте, это плохо». Я думаю, что важно знать инструменты и уметь оценивать риск их использования, и соответственно применять.
Попробую внести ясность, мне вообще не понравилось, что автор этой статьи склоняет к использованию Service Locator'a.

Service Locator — это не DI в смысле Dependency Injection, а это DI в смысле Dependency Inversion.

Dependency Injection — Это подача зависимости классу извне. Простыми словами звучит так: не инстанциируй сущности, не предназначенные для инстанцииорвания сущностей, внутри других сущностей, но если нужно инстанциирование сущностей — используй для этого специализированные сущности (фабрики, например).
Сервис-локатор же получает некий сервис «изнутри» класса. Он как бы говорит, что «мне нужно это, дайте немедленно». Хорошим подходом же является декларативное объявление требований зависимостей. Не используя никакие сервис локаторы, просто объявляем в конструкторе, что нам нужно. Для решения таких зависимостей нам поможет Inversion of Control Contrainer. Ninject, кстати, им является.

Dependency Inversion — это связывание модулей нижнего уровня на высоком уровне. Одним из вариантов которого является связывание реализаций при помощи интерфейсов. Поэтому Service Locator можно к этому отнести.

Service Locator является Dependency Inversion, Dependency Injection является Dependency Inversion, но Service Locator это не Dependency Injection.

Когда вы используете Service Locator внутри сущности, вы отвязываетесь от других зависимостей по интерфейсу. Но при этом привязываетесь реализацией к сервис-локатору. Это не является декаплингом (decoupling) зависимостей, и прежде всего поэтому это антипаттерн. Подробнее можно почитать на википедии, например. Английской, конечно же.

Наиболее удобным путем внедрения зависимостей является использование Inversion of Control контейнера, коим и является Ninject. Когда вы используете IoCC, вы не используете в своих классах некие сомнительные сторонние сервисы, синглторы, депенденси резолверы и сервис-локаторы. IoCC позволяют настраивать время жизни объектов. Они сами решают, что нужно запрашиваемым сущностям, и делают это в рантайме.

ASP.NET MVC позволяет перегружать фабрику контроллеров своей, поэтому осуществить депенденси резолвинг контроллеров можно там. И все, это первое и последнее место, где вы использовали IoCC напрямую. Во всех других местах всё начнет работать автоматически.

Мне из всех IoCC контейнеров под .NET больше всего нравится Castle Windsor. Он очень чистый, не мешается с вашим кодом и постоянно развивается. Можете почитать пример внедрения Castle Windsor в ваше ASP.NET MVC приложение.
Спасибо за исчерпывающий ответ. Теперь увидел разницу. Если мне не изменяет память то в плане юнити использование IoC например по средствам работы с конструкторами по сравнению с Service Locator будет немного более медленным особенно там где будет присутствовать вложенная инициализация. Но в целом подход намного чище чем Service Locator.
Быстродействие зависит от самого IoCC. Castle Windsor, например, узнает требуемые зависимости через рефлексию. В Autofac же, их нужно прописывать явно. Конечно, в этом случае Autofac будет быстрее, но все равно это не bottleneck в приложении. :)
ещё вопрос, вероятно глупый: почему Autofac и Castle вынесены в скобки после Unity? Разве все реализации не самодостаточны?
Самодостаточны, просто я с ними не работал и это как бы внутренне для меня они второстепенны. Поправил.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории