В чем разница между шаблонами Dependency Injection и Service Locator?

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

Внедрение зависимостей (DI), похоже, использует конструктор или установщик для «внедрения» своих зависимостей.

Пример использования Constructor Injection:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = 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-контейнеры в основном, когда у нас много зависимостей?

13.10.2009 01:15:19
Инверсия управления означает, что «объект не должен знать, как построить свои зависимости»?!? Этот новый для меня. Нет, правда, это не то, что означает «инверсия контроля». См. Martinfowler.com/bliki/InversionOfControl.html В этой статье даже приводятся ссылки на этимологию этого термина, начиная с 1980-х годов.
Rogério 18.02.2010 01:34:19
Mark Seemann 27.11.2013 08:24:45
Марк Симанн утверждает, что Service Locator является анти-паттерном ( blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern ). Кроме того, я нашел диаграмму (найденную здесь, stackoverflow.com/a/9503612/1977871 ) полезной для понимания затруднений DI и SL. Надеюсь это поможет.
VivekDev 11.01.2016 08:12:55
15 ОТВЕТОВ

Когда вы используете локатор службы, каждый класс будет зависеть от вашего локатора службы. Это не относится к внедрению зависимостей. Инжектор зависимостей обычно вызывается только один раз при запуске, чтобы внедрить зависимости в некоторый основной класс. Классы, от которых зависит этот основной класс, будут рекурсивно внедряться в свои зависимости, пока у вас не появится полный граф объектов.

Хорошее сравнение: http://martinfowler.com/articles/injection.html

Если ваш инжектор зависимостей выглядит как сервисный локатор, где классы напрямую вызывают инжектор, то, вероятно, это не инжектор зависимостей, а скорее сервисный локатор.

91
13.10.2009 01:28:13
Но как вы справляетесь со случаем, когда вам нужно создавать объекты во время выполнения? Если вы создаете их вручную с помощью «new», вы не можете использовать DI. Если вы вызываете DI-фреймворк за помощью, вы нарушаете схему. Так какие варианты остались?
Boris 10.04.2013 14:35:22
@ Борис У меня была та же проблема, и я решил ввести фабрики для конкретных классов. Не красиво, но сделал работу. Хотелось бы увидеть более красивое решение.
Charlie Rudenstål 9.01.2014 11:43:02
Прямая ссылка сравнения: martinfowler.com/articles/...
Teoman shipahi 3.08.2015 19:14:51
@ Борис Если бы мне нужно было создавать новые объекты на лету, я бы добавлял абстрактную фабрику для указанных объектов. Это было бы похоже на внедрение сервисного локатора в этом случае, но обеспечивает конкретный, унифицированный интерфейс времени компиляции для создания соответствующих объектов и делает зависимости явными.
LivePastTheEnd 27.02.2017 08:35:56

Разница может показаться незначительной, но даже с ServiceLocator класс по-прежнему отвечает за создание своих зависимостей. Это просто использует сервисный локатор, чтобы сделать это. С DI класс получает свои зависимости. Он не знает и не заботится, откуда они берутся. Одним из важных результатов этого является то, что пример DI намного проще для модульного тестирования - потому что вы можете передать ему ложные реализации его зависимых объектов. Вы можете объединить два - и ввести сервисный локатор (или фабрику), если хотите.

174
20.02.2019 16:10:00
Кроме того, вы можете использовать оба при создании класса. Конструктор по умолчанию может использовать SL для извлечения зависимостей и передачи их «реальному» конструктору, который получает эти зависимости. Вы получаете лучшее из обоих миров.
Grant Palin 15.10.2009 17:15:36
Нет, ServiceLocator является ответственным за создание правильной реализации для данной зависимости (плагин). В случае DI ответственность за это несет DI-контейнер.
Rogério 18.02.2010 01:37:27
@Rogerio да, но класс все еще должен знать о Service Locator ... это две зависимости. Также чаще всего я видел делегирование Service Locator в контейнер DI для поиска, особенно для временных объектов, которые нуждаются в поддержке сервиса.
Adam Gent 6.02.2013 23:08:08
@ Adam Я не говорил, что локатор служб будет делегировать контейнеру DI. Это два взаимоисключающих паттерна, как описано в «официальной» статье . Для меня Service Locator имеет огромное преимущество перед DI на практике: использование контейнера DI порождает злоупотребления (которые я неоднократно видел), а использование Service Locator - нет.
Rogério 7.02.2013 11:13:26
«Одним из важных результатов этого является то, что пример DI намного проще для модульного тестирования - потому что вы можете передать ему ложные реализации его зависимых объектов». Не правда. В ваших модульных тестах можно использовать вызов функции регистра в контейнере локатора службы, чтобы легко добавлять макеты в реестр.
Drumbeg 21.03.2014 10:15:16

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

Более того, локаторы служб нарушают инкапсуляцию, потому что они предоставляют глобальную точку доступа к зависимостям других объектов. С сервисным локатором, как с любым синглтоном :

становится затруднительным задавать предварительные и последующие условия для интерфейса объекта клиента, потому что на работу по его реализации можно вмешиваться извне.

С внедрением зависимостей, как только зависимости объекта определены, они находятся под контролем самого объекта.

50
17.12.2009 16:53:18
Я предпочитаю «Синглтоны считаются глупыми
Charles Graham 13.10.2009 02:05:34
Я люблю старого Стива Йегге, и заголовок этой статьи великолепен, но я думаю, что цитируемая мной статья и «Синглтоны - патологические лжецы» Мишко Хевери ( misko.hevery.com/2008/08/17/singletons-are-pathological- лжецы ) лучше рассуждать против конкретной чертовщины сервисного локатора.
Jeff Sternal 23.10.2009 19:49:15
Этот ответ является наиболее правильным, поскольку он лучше всего определяет локатор службы: «Класс, который скрывает свои зависимости». Обратите внимание, что внутреннее создание зависимости, хотя и не очень хорошая вещь, не делает класс локатором службы. Кроме того, получение зависимости от контейнера является проблемой, но не «проблемой», которая наиболее четко определяет локатор службы.
Sam 15.05.2018 15:21:06
With dependency injection (at least constructor injection) the dependencies are explicit., Пожалуйста, объясни.
FindOutIslamNow 13.06.2018 11:51:20
Как и выше, я не вижу, как SL делает зависимости менее явными, чем DI ...
Michał Powłoka 29.10.2018 09:17:46

Класс, использующий конструктор DI, указывает потребителю кода, что существуют зависимости, которые должны быть удовлетворены. Если класс использует SL для извлечения таких зависимостей, потребляющий код не знает о зависимостях. На первый взгляд это может показаться лучше, но на самом деле полезно знать о любых явных зависимостях. Это лучше с архитектурной точки зрения. И при выполнении тестирования вы должны знать, нужны ли классу определенные зависимости, и настроить SL для предоставления соответствующих поддельных версий этих зависимостей. С DI, просто пройти в подделках. Не большая разница, но она есть.

Хотя DI и SL могут работать вместе. Полезно иметь центральное расположение для общих зависимостей (например, настройки, регистратор и т. Д.). Учитывая класс, использующий такие deps, вы можете создать «реальный» конструктор, который получает deps, и конструктор по умолчанию (без параметров), который извлекает из SL и пересылает в «реальный» конструктор.

РЕДАКТИРОВАТЬ: и, конечно, когда вы используете SL, вы вводите некоторую связь с этим компонентом. Что иронично, поскольку идея такой функциональности заключается в поощрении абстракций и уменьшении связи. Проблемы могут быть сбалансированы, и это зависит от того, сколько мест вам нужно будет использовать SL. Если все сделано как предложено выше, просто в конструкторе класса по умолчанию.

22
14.10.2009 17:51:01
Интересно! Я использую DI и SL, но не с двумя конструкторами. Три или четыре наиболее скучных часто необходимых зависимости (настройки и т. Д.) Получаются из SL на лету. Все остальное вводится конструктором. Это немного некрасиво, но прагматично.
maaartinus 19.11.2017 14:32:49

Я думаю, что оба работают вместе.

Внедрение зависимостей означает, что вы помещаете некоторый зависимый класс / интерфейс в потребляющий класс (обычно в его конструктор). Это разъединяет два класса через интерфейс и означает, что потребляющий класс может работать со многими типами реализаций «внедренной зависимости».

Роль сервисного локатора - собрать вашу реализацию. Вы устанавливаете сервисный локатор через некоторое загрузочное связывание в начале вашей программы. Начальная загрузка - это процесс связывания типа реализации с конкретным абстрактным / интерфейсом. Который создается для вас во время выполнения. (на основе вашей конфигурации или начальной загрузки). Если бы вы не внедрили внедрение зависимостей, было бы очень трудно использовать локатор служб или контейнер IOC.

6
19.02.2011 14:17:22

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

Я знаю разницу между локатором службы (теперь он рассматривается как анти-шаблон) и шаблонами внедрения зависимостей и могу понять конкретные примеры каждого шаблона, но меня смутили примеры, показывающие локатор службы внутри конструктора (предположим, что мы ' делаю конструктор инъекций).

«Сервисный локатор» часто используется как в качестве имени шаблона, так и в качестве имени для ссылки на объект (предположим, тоже), который используется в этом шаблоне для получения объектов без использования оператора new. Теперь этот же тип объекта можно также использовать в корне композиции для выполнения внедрения зависимостей, и здесь возникает путаница.

Следует отметить, что вы можете использовать объект локатора службы внутри конструктора DI, но вы не используете «шаблон локатора службы». Это менее запутанно, если вместо этого кто-то называет его контейнерным объектом IoC, так как вы, возможно, догадались, что они по сути делают то же самое (исправьте меня, если я ошибаюсь).

Называется ли он локатором службы (или просто локатором) или контейнером IoC (или просто контейнером), не имеет значения, как вы уже догадались, поскольку они, вероятно, ссылаются на одну и ту же абстракцию (исправьте меня, если я ошибаюсь) ). Просто называя его локатором сервисов, можно предположить, что каждый использует анти-паттерн сервисного локатора вместе с паттерном внедрения зависимостей.

ИМХО, называя его «локатором» вместо «местоположения» или «определения местоположения», иногда можно подумать, что локатор службы в статье ссылается на контейнер локатора службы, а не на шаблон локатора службы (анти-) особенно когда есть связанный паттерн, называемый Dependency Injection, а не Dependency Injector.

4
10.01.2012 08:07:53

Мартин Фаулер заявляет :

С помощью сервисного локатора класс приложения явно запрашивает это посредством сообщения локатору. При внедрении нет явного запроса, сервис появляется в классе приложения - отсюда и инверсия управления.

Вкратце: Service Locator и Dependency Injection являются просто реализациями принципа инверсии зависимости.

Важным принципом является «Зависимость от абстракций, а не от конкреций». Это сделает ваш программный дизайн «слабосвязанным», «расширяемым», «гибким».

Вы можете использовать тот, который наилучшим образом соответствует вашим потребностям. Для большого приложения, имеющего огромную кодовую базу, вам лучше использовать Service Locator, потому что Dependency Injection потребует больше изменений в вашей кодовой базе.

Вы можете проверить это сообщение: Инверсия зависимости: Сервисный локатор или Инъекция зависимости

Также классика: Инверсия Контейнеров Контроля и шаблон Внедрения Зависимостей. Автор Martin Fowler

Проектирование Многоразовых Классов Ральфом Джонсоном & Брайаном Футом

Тем не менее, тот, который открыл мне глаза, был: ASP.NET MVC: разрешить или ввести? Это проблема ... Дино Эспозито

37
27.12.2018 16:29:25
Фантастическое резюме: «Сервисный локатор и внедрение зависимостей - всего лишь реализации принципа инверсии зависимостей».
Hans 4.10.2015 11:42:24
И он также заявляет: ключевое отличие состоит в том, что с локатором службы каждый пользователь службы имеет зависимость от локатора. Локатор может скрывать зависимости от других реализаций, но вам нужно видеть локатор. Таким образом, решение между локатором и инжектором зависит от того, является ли эта зависимость проблемой.
programaths 29.06.2016 07:19:00
ServiceLocator и DI не имеют ничего общего с «принципом инверсии зависимости» (DIP). DIP - это способ сделать высокоуровневый компонент более пригодным для повторного использования, заменив зависимость времени компиляции от низкоуровневого компонента зависимостью от абстрактного типа, определенного вместе с высокоуровневым компонентом, который реализуется низкоуровневым компонентом. компонент уровня; таким образом, зависимость времени компиляции инвертируется, так как теперь это низкоуровневая зависимость, которая зависит от высокоуровневой. Также обратите внимание, что в статье Мартина Фаулера объясняется, что DI и IoC - это не одно и то же.
Rogério 26.09.2016 15:44:00

Одна из причин, чтобы добавить, вдохновленная обновлением документации, которую мы написали для проекта MEF на прошлой неделе (я помогаю строить MEF).

Как только приложение состоит из потенциально тысяч компонентов, может быть трудно определить, может ли конкретный компонент быть правильно создан. Под «созданным правильно» я подразумеваю, что в этом примере, основанном на Fooкомпоненте, экземпляр IBarи будет доступен, и что компонент, обеспечивающий его, будет:

  • иметь необходимые зависимости,
  • не участвовать в каких-либо недопустимых циклах зависимости, и
  • в случае MEF предоставляется только один экземпляр.

Во втором примере, который вы привели, когда конструктор обращается к контейнеру IoC для получения его зависимостей, единственный способ проверить, что экземпляр Fooбудет способен быть правильно создан с фактической конфигурацией времени выполнения вашего приложения, - это фактически создать это .

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

Корень этой проблемы - это различие, уже вызванное @Jon: внедрение зависимостей через конструктор является декларативным, тогда как во второй версии используется обязательный шаблон Service Locator.

Контейнер IoC, при аккуратном использовании, может статически анализировать конфигурацию среды выполнения вашего приложения, фактически не создавая экземпляров задействованных компонентов. Многие популярные контейнеры предоставляют некоторые варианты этого; Microsoft.Composition , версия MEF для веб-приложений .NET 4.5 и приложений в стиле Metro, предоставляет CompositionAssertпример из вики-документации. Используя его, вы можете написать код:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

(См. Этот пример ).

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

Надеюсь, что это интересное дополнение к этому всестороннему набору ответов по теме!

6
10.12.2016 16:09:16

В этом упрощенном случае нет никакой разницы, и они могут использоваться взаимозаменяемо. Однако проблемы реального мира не так просты. Просто предположим, что у самого класса Bar есть другая зависимость с именем D. В этом случае ваш локатор службы не сможет разрешить эту зависимость, и вам придется создать его экземпляр в классе D; потому что ваши классы несут ответственность за создание их зависимостей. Было бы еще хуже, если бы у самого класса D были другие зависимости, а в реальных ситуациях это обычно становилось еще сложнее. В таких случаях DI является лучшим решением, чем ServiceLocator.

4
31.05.2016 12:28:05
Хм, я бы не согласился: сервис локатор отл. ясно показывает, что там все еще есть зависимость ... локатор службы. Если у самого barкласса есть зависимость, у него barтакже будет локатор службы, и в этом весь смысл использования DI / IoC.
GFoley83 10.05.2014 00:49:19

Оба они являются методами реализации IoC. Есть и другие шаблоны, которые реализуют Inversion of Control:

  • заводская модель
  • сервисный локатор
  • внедрение зависимостей (внедрение конструкторов, внедрение параметров (если не требуется), внедрение сеттеров внедрения интерфейса) ...

Сервисный локатор и DI кажутся более похожими, оба они используют контейнер для определения зависимостей, которые отображают абстракцию для конкретной реализации.

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

7
22.02.2015 21:03:33

В моем последнем проекте я использую оба. Я использую инъекцию зависимостей для модульной тестируемости. Я использую сервисный локатор, чтобы скрыть реализацию и зависимость от моего контейнера IoC. и да! Как только вы используете один из контейнеров IoC (Unity, Ninject, Windsor Castle), вы зависите от него. И как только он устареет или по какой-то причине, если вы захотите поменять его местами, вам придется / может потребоваться изменить реализацию - по крайней мере, корневую композицию. Но сервисный локатор абстрагирует этот этап.

Как бы вы не зависели от вашего контейнера 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/

7
3.08.2015 19:33:13
Ссылка на статью в Johandekoning не работает.
JakeJ 26.01.2018 23:12:34

В чем разница (если есть) между внедрением зависимостей и поиском сервисов? Оба шаблона хороши в реализации принципа инверсии зависимости. Шаблон Service Locator легче использовать в существующей кодовой базе, поскольку он делает общий дизайн более свободным, не вызывая изменений в общедоступном интерфейсе. По этой же причине код, основанный на шаблоне локатора службы, менее читабелен, чем эквивалентный код, основанный на внедрении зависимостей.

Паттерн Dependency Injection проясняет, так как сигнатура имеет зависимости, которые будут иметь класс (или метод). По этой причине полученный код более чистый и более читаемый.

2
23.01.2017 11:30:59

Для записи

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Если вам не нужен интерфейс (интерфейс используется более чем одним классом), вы НЕ ДОЛЖНЫ ИСПОЛЬЗОВАТЬ его . В этом случае IBar позволяет использовать любой класс обслуживания, который его реализует. Однако обычно этот интерфейс будет использоваться одним классом.

Почему плохая идея использовать интерфейс? Потому что это действительно трудно отлаживать.

Например, допустим, что экземпляр «bar» не удался, вопрос: какой класс не удался? Какой код я должен исправить? Простое представление, оно ведет к интерфейсу, и именно здесь заканчивается моя дорога.

Вместо этого, если код использует жесткую зависимость, легко отладить ошибку.

//Foo Needs an IBar
public class Foo
{
  private BarService bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Если «bar» не срабатывает, я должен проверить и запустить класс BarService.

-3
30.09.2018 00:47:21
Класс - это план для создания конкретного объекта. С другой стороны, интерфейс является a contractи просто определяет поведение, а не действие. Вместо того, чтобы обойти реальный объект, только интерфейс используется совместно, так что потребитель не имеет доступа к остальной части вашего объекта. Также для модульного тестирования это помогает протестировать только ту часть, которая должна быть проверена. Думаю, со временем вы поймете его полезность.
Gunhan 29.04.2020 07:57:53

Следующая простая концепция дала мне более четкое понимание разницы между SL и DI (я просто помню слова, выделенные жирным шрифтом):

SL используется в потребителе и извлекает услуги по идентификатору из некоторого хранилища по прямому запросу потребителя; DI находится где-то снаружи, и он берет сервисы из некоторого хранилища и передает их потребителю (независимо от того, конструктор или метод)

Тем не менее, мы можем говорить о различии между ними только в контексте конкретного использования потребителем; когда SL и DI используются в корне композиции, они почти одинаковы.

0
14.11.2019 08:11:23

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

0
21.04.2020 20:30:31