Обе модели кажутся реализацией принципа инверсии управления. То есть объект не должен знать, как построить свои зависимости.
Внедрение зависимостей (DI), похоже, использует конструктор или установщик для «внедрения» своих зависимостей.
Пример использования Constructor Injection:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
//...
}
Сервисный локатор, по-видимому, использует «контейнер», который связывает свои зависимости и дает foo его bar.
Пример использования сервисного локатора:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Поскольку наши зависимости являются просто самими объектами, эти зависимости имеют зависимости, которые имеют еще больше зависимостей, и так далее, и тому подобное. Таким образом, Инверсия Контейнера Контроля (или Контейнера DI) родилась. Примеры: Замок Виндзор, Нинъект, Карта структуры, Весна и т. Д.)
Но Контейнер IOC / DI выглядит точно так же, как Сервисный Локатор. Называет ли это контейнер DI плохим именем? Контейнер IOC / DI - это просто другой тип сервисного локатора? Есть ли нюанс в том, что мы используем DI-контейнеры в основном, когда у нас много зависимостей?
источник
Ответы:
Разница может показаться незначительной, но даже с ServiceLocator, класс по-прежнему отвечает за создание своих зависимостей. Это просто использует сервисный локатор, чтобы сделать это. С помощью DI класс получает свои зависимости. Он не знает и не заботится о том, откуда они берутся. Одним из важных результатов этого является то, что пример DI гораздо проще для модульного тестирования - потому что вы можете передать ему ложные реализации его зависимых объектов. Вы можете объединить два - и ввести сервисный локатор (или фабрику), если хотите.
источник
Когда вы используете локатор службы, каждый класс будет зависеть от вашего локатора службы. Это не относится к внедрению зависимостей. Инжектор зависимостей обычно вызывается только один раз при запуске, чтобы внедрить зависимости в некоторый основной класс. Классы, от которых зависит этот основной класс, будут рекурсивно внедряться в свои зависимости, пока у вас не появится полный граф объектов.
Хорошее сравнение: http://martinfowler.com/articles/injection.html
Если ваш инжектор зависимостей выглядит как сервисный локатор, где классы напрямую вызывают инжектор, то это, вероятно, не инжектор зависимостей, а скорее сервисный локатор.
источник
Локаторы служб скрывают зависимости - вы не можете определить, глядя на объект, попадает ли он в базу данных или нет (например), когда он получает соединения от локатора. С внедрением зависимости (по крайней мере, с помощью конструктора) зависимости являются явными.
Более того, локаторы служб нарушают инкапсуляцию, потому что они предоставляют глобальную точку доступа к зависимостям других объектов. С сервисным локатором, как с любым синглтоном :
С внедрением зависимостей, как только зависимости объекта определены, они находятся под контролем самого объекта.
источник
With dependency injection (at least constructor injection) the dependencies are explicit.
, Пожалуйста, объясни.Мартин Фаулер заявляет :
Вкратце: Service Locator и Dependency Injection являются просто реализациями принципа инверсии зависимости.
Важный принцип: «Зависит от абстракций, а не от конкреций». Это сделает ваш программный дизайн «слабосвязанным», «расширяемым», «гибким».
Вы можете использовать тот, который наилучшим образом соответствует вашим потребностям. Для большого приложения, имеющего огромную кодовую базу, вам лучше использовать Service Locator, потому что Dependency Injection потребует больше изменений в вашей кодовой базе.
Вы можете проверить это сообщение: Инверсия зависимостей: Поиск сервисов или Внедрение зависимостей
Также классика: Инверсия Контейнеров Контроля и Шаблон Внедрения Зависимостей. Автор Martin Fowler
Проектирование Многоразовых Классов Ральфом Джонсоном & Брайаном Футом
Тем не менее, тот, который открыл мне глаза, был: ASP.NET MVC: разрешить или ввести? Это проблема ... Дино Эспозито
источник
Класс, использующий конструктор DI, указывает потребителю кода, что существуют зависимости, которые должны быть удовлетворены. Если класс использует SL для извлечения таких зависимостей, потребляющий код не знает о зависимостях. На первый взгляд это может показаться лучше, но на самом деле полезно знать о любых явных зависимостях. Это лучше с архитектурной точки зрения. И при проведении тестирования вы должны знать, нужны ли классу определенные зависимости, и настроить SL для предоставления соответствующих поддельных версий этих зависимостей. С DI, просто пройти в подделках. Не большая разница, но она есть.
Хотя DI и SL могут работать вместе. Полезно иметь центральное расположение для общих зависимостей (например, настройки, регистратор и т. Д.). Учитывая класс, использующий такие deps, вы можете создать «реальный» конструктор, который получает deps, и конструктор по умолчанию (без параметров), который извлекает из SL и пересылает в «реальный» конструктор.
РЕДАКТИРОВАТЬ: и, конечно, когда вы используете SL, вы вводите некоторую связь с этим компонентом. Что иронично, поскольку идея такой функциональности заключается в поощрении абстракций и уменьшении связи. Проблемы могут быть сбалансированы, и это зависит от того, сколько мест вам нужно будет использовать SL. Если все сделано как предложено выше, просто в конструкторе класса по умолчанию.
источник
Оба они являются методами реализации IoC. Есть и другие шаблоны, которые реализуют Inversion of Control:
Сервисный локатор и DI-контейнер кажутся более похожими, оба используют контейнер для определения зависимостей, которые отображают абстракцию в конкретной реализации.
Основное различие заключается в том, как расположены зависимости: в Service Locator клиентский код запрашивает зависимости, в DI-контейнере мы используем контейнер для создания всех объектов и внедряет зависимость в качестве параметров конструктора (или свойств).
источник
В моем последнем проекте я использую оба. Я использую инъекцию зависимостей для модульной тестируемости. Я использую сервисный локатор, чтобы скрыть реализацию и зависимость от моего контейнера IoC. и да! Как только вы используете один из контейнеров IoC (Unity, Ninject, Windsor Castle), вы зависите от него. И как только он устареет или по какой-то причине, если вы захотите поменять его местами, вам придется / может потребоваться изменить вашу реализацию - по крайней мере, композицию root. Но сервисный локатор абстрагирует этот этап.
Как бы вы не зависели от вашего контейнера IoC? Либо вам нужно будет обернуть его самостоятельно (что является плохой идеей), либо вы используете Service Locator для настройки контейнера IoC. Таким образом, вы скажете локатору службы, чтобы получить какой интерфейс вам нужен, и он вызовет контейнер IoC, настроенный для получения этого интерфейса.
В моем случае я использую ServiceLocator, который является компонентом фреймворка. И использовать Unity для контейнера IoC. Если в будущем мне нужно поменять контейнер IoC на Ninject, все, что мне нужно сделать, - это настроить мой локатор служб для использования Ninject вместо Unity. Легкая миграция.
Вот отличная статья, объясняющая этот сценарий; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/
источник
Я думаю, что оба работают вместе.
Внедрение зависимостей означает, что вы помещаете некоторый зависимый класс / интерфейс в потребляющий класс (обычно в его конструктор). Это разъединяет два класса через интерфейс и означает, что потребляющий класс может работать со многими типами реализаций «внедренной зависимости».
Роль сервисного локатора - собрать вашу реализацию. Вы устанавливаете сервисный локатор с помощью некоторой загрузки в начале вашей программы. Начальная загрузка - это процесс привязки типа реализации к конкретному абстрактному / интерфейсу. Который создается для вас во время выполнения. (в зависимости от вашей конфигурации или начальной загрузки). Если бы вы не реализовали внедрение зависимостей, было бы очень трудно использовать локатор служб или контейнер IOC.
источник
Одна из причин, чтобы добавить, вдохновленная обновлением документации, которое мы написали для проекта MEF на прошлой неделе (я помогаю строить MEF).
Как только приложение состоит из потенциально тысяч компонентов, может быть трудно определить, может ли конкретный компонент быть правильно создан. Под «созданным правильно» я подразумеваю, что в этом примере, основанном на
Foo
компоненте, экземплярIBar
и будет доступен, и что компонент, обеспечивающий его, будет:Во втором примере, который вы привели, когда конструктор обращается к контейнеру IoC для извлечения его зависимостей, единственный способ проверить
Foo
правильность создания экземпляра в реальной конфигурации времени выполнения вашего приложения - это фактически создать это .Это имеет все виды неудобных побочных эффектов во время тестирования, потому что код, который будет работать во время выполнения, не обязательно будет работать в тестовом окружении. Mocks не подойдет, потому что реальная конфигурация - это то, что нам нужно тестировать, а не какая-то настройка во время тестирования.
Корень этой проблемы - это различие, уже вызванное @Jon: внедрение зависимостей через конструктор является декларативным, в то время как вторая версия использует императивный шаблон Service Locator.
Контейнер IoC, при аккуратном использовании, может статически анализировать конфигурацию среды выполнения вашего приложения, фактически не создавая экземпляров задействованных компонентов. Многие популярные контейнеры предоставляют некоторые варианты этого; Microsoft.Composition , версия MEF для веб-приложений .NET 4.5 и приложений в стиле Metro, предоставляет
CompositionAssert
пример из вики-документации. Используя его, вы можете написать код:(См. Этот пример ).
Проверяя составные корни вашего приложения во время тестирования, вы потенциально можете обнаружить некоторые ошибки, которые в противном случае могут просочиться через тестирование в дальнейшем.
Надеюсь, что это интересное дополнение к этому всестороннему набору ответов по теме!
источник
Примечание: я не совсем отвечаю на вопрос. Но я чувствую, что это может быть полезно для новых изучающих шаблон внедрения зависимостей, которые путают его с шаблоном сервисного локатора (анти), которые случайно попадают на эту страницу.
Я знаю разницу между локатором службы (теперь он рассматривается как анти-шаблон) и шаблонами внедрения зависимостей и могу понять конкретные примеры каждого шаблона, но меня смутили примеры, показывающие локатор службы внутри конструктора (предположим, что мы ' делаю конструктор инъекций).
«Сервисный локатор» часто используется как в качестве имени шаблона, так и в качестве имени для ссылки на объект (предположим, тоже), который используется в этом шаблоне для получения объектов без использования оператора new. Теперь этот же тип объекта можно также использовать в корне композиции для выполнения внедрения зависимостей, и здесь возникает путаница.
Следует отметить, что вы можете использовать объект локатора службы внутри конструктора DI, но вы не используете «шаблон локатора службы». Это менее запутанно, если вместо этого кто-то называет его объектом-контейнером IoC, поскольку вы, возможно, догадались, что они, по сути, делают то же самое (исправьте меня, если я ошибаюсь).
Называется ли он локатором службы (или просто локатором) или контейнером IoC (или просто контейнером), не имеет значения, как вы уже догадались, поскольку они, вероятно, ссылаются на одну и ту же абстракцию (исправьте меня, если я ошибаюсь) ). Просто называя его локатором сервисов, можно предположить, что каждый использует анти-паттерн сервисного локатора вместе с паттерном внедрения зависимостей.
ИМХО, называя его «локатором» вместо «местоположения» или «определения местоположения», можно также иногда думать, что локатор службы в статье ссылается на контейнер локатора службы, а не на шаблон локатора службы (анти-) особенно когда есть связанный паттерн, который называется Dependency Injection, а не Dependency Injector.
источник
В этом упрощенном случае нет никакой разницы, и они могут использоваться взаимозаменяемо. Однако проблемы реального мира не так просты. Просто предположим, что у самого класса Bar есть другая зависимость с именем D. В этом случае ваш локатор службы не сможет разрешить эту зависимость, и вам придется создать его экземпляр в классе D; потому что ваши классы несут ответственность за создание их зависимостей. Было бы еще хуже, если бы у самого класса D были другие зависимости, а в реальных ситуациях это обычно становилось еще сложнее. В таких случаях DI является лучшим решением, чем ServiceLocator.
источник
bar
класса есть зависимость, у негоbar
также будет локатор службы, в этом весь смысл использования DI / IoC.В чем разница (если есть) между внедрением зависимостей и поиском сервисов? Оба шаблона хороши в реализации принципа инверсии зависимости. Шаблон Service Locator легче использовать в существующей кодовой базе, поскольку он делает общий дизайн более свободным, не вызывая изменений в общедоступном интерфейсе. По этой же причине код, основанный на шаблоне локатора службы, менее читаем, чем эквивалентный код, основанный на внедрении зависимостей.
Паттерн Dependency Injection проясняет, так как сигнатура имеет зависимости, которые будут иметь класс (или метод). По этой причине полученный код становится чище и более читабельным.
источник
Следующая простая концепция дала мне более четкое понимание различий между Service Locator и DI Container:
Сервисный локатор используется потребителем и извлекает сервисы по идентификатору из некоторого хранилища по прямому запросу потребителя.
Контейнер DI находится где-то снаружи, он берет сервисы из некоторого хранилища и передает их потребителю (независимо от того, конструктор или метод)
Тем не менее, мы можем говорить о различии между ними только в контексте конкретного использования потребителем. При использовании Service Locator и DI Container в корне композиции они почти одинаковы.
источник
DI-контейнер - это расширенный набор сервисных локаторов. Он может использоваться для определения местоположения службы с дополнительной возможностью сборки (подключения) инъекций зависимости .
источник
Для записи
Если вам действительно не нужен интерфейс (интерфейс используется более чем одним классом), вы НЕ ДОЛЖНЫ ИСПОЛЬЗОВАТЬ его . В этом случае IBar позволяет использовать любой класс обслуживания, который его реализует. Однако обычно этот интерфейс будет использоваться одним классом.
Почему плохая идея использовать интерфейс? Потому что это действительно сложно отлаживать.
Например, допустим, что экземпляр «bar» не удался, вопрос: какой класс не удался? Какой код я должен исправить? Простое представление, оно ведет к интерфейсу, и именно здесь заканчивается моя дорога.
Вместо этого, если код использует жесткую зависимость, легко отладить ошибку.
Если «bar» не срабатывает, я должен проверить и запустить класс BarService.
источник
contract
и просто определяет поведение, а не действие. Вместо того, чтобы обойти реальный объект, только интерфейс используется совместно, чтобы потребитель не получил доступ к остальной части вашего объекта. Также для модульного тестирования это помогает протестировать только ту часть, которая должна быть проверена. Думаю, со временем вы поймете его полезность.