Недавно я прочитал статью Марка Симанна об анти-паттерне Service Locator.
Автор указывает на две основные причины, почему ServiceLocator является анти-паттерном:
Проблема использования API (с которой у меня все в порядке)
Когда в классе используется локатор Service, очень трудно увидеть его зависимости, поскольку в большинстве случаев у класса есть только один конструктор PARAMETERLESS. В отличие от ServiceLocator, подход DI явно раскрывает зависимости через параметры конструктора, поэтому зависимости легко увидеть в IntelliSense.Вопрос обслуживания (который меня озадачивает).
Рассмотрим следующий пример.
У нас есть класс «MyType», который использует подход локатора службы:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Теперь мы хотим добавить еще одну зависимость в класс 'MyType'
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
И здесь начинается мое недоразумение. Автор говорит:
Становится намного сложнее сказать, вводите ли вы критические изменения или нет. Вы должны понимать все приложение, в котором используется локатор служб, и компилятор вам не поможет.
Но подождите секунду, если бы мы использовали подход DI, мы бы ввели зависимость с другим параметром в конструктор (в случае внедрения конструктора). И проблема все еще будет там. Если мы можем забыть настроить ServiceLocator, тогда мы можем забыть добавить новое отображение в наш контейнер IoC, и подход DI будет иметь ту же проблему во время выполнения.
Также автор упомянул о сложностях модульного тестирования. Но разве у нас не будет проблем с подходом DI? Не нужно ли нам обновить все тесты, которые создавали этот класс? Мы обновим их так, чтобы они передавали новую ложную зависимость, чтобы сделать наш тест компилируемым. И я не вижу никакой пользы от этого обновления и затрат времени.
Я не пытаюсь защищать подход Service Locator. Но это недоразумение заставляет меня думать, что я теряю что-то очень важное. Может ли кто-нибудь развеять мои сомнения?
ОБНОВЛЕНИЕ (РЕЗЮМЕ):
Ответ на мой вопрос «Является ли Service Locator антишаблоном» действительно зависит от обстоятельств. И я определенно не предложил бы вычеркнуть это из вашего списка инструментов. Это может стать очень удобным, когда вы начнете работать с устаревшим кодом. Если вам повезло оказаться в самом начале вашего проекта, то подход DI может быть лучшим выбором, поскольку он имеет некоторые преимущества по сравнению с Service Locator.
И вот основные отличия, которые убедили меня не использовать Service Locator для моих новых проектов:
- Самое очевидное и важное: Service Locator скрывает зависимости классов
- Если вы используете какой-либо контейнер IoC, он, вероятно, будет сканировать весь конструктор при запуске, чтобы проверить все зависимости и дать вам немедленную обратную связь по отсутствующим сопоставлениям (или неправильной конфигурации); это невозможно, если вы используете свой контейнер IoC в качестве локатора службы
Для подробностей читайте отличные ответы, которые приведены ниже.
Ответы:
Если вы определяете шаблоны как анти-шаблоны только потому, что в некоторых ситуациях они не подходят, тогда ДА - это анти-шаблон. Но с этим рассуждением все паттерны также будут анти-паттернами.
Вместо этого мы должны посмотреть, действительно ли используются шаблоны, и для Service Locator есть несколько вариантов использования. Но давайте начнем с рассмотрения приведенных вами примеров.
Кошмар обслуживания этого класса заключается в том, что зависимости скрыты. Если вы создаете и используете этот класс:
Вы не понимаете, что у него есть зависимости, если они скрыты, используя расположение службы. Теперь, если мы вместо этого используем внедрение зависимости:
Вы можете непосредственно определить зависимости и не можете использовать классы до их удовлетворения.
В типичном бизнес-приложении вы должны избегать использования местоположения службы именно по этой причине. Это должен быть шаблон для использования, когда нет других вариантов.
Является ли паттерн анти-паттерном?
Нет.
Например, инверсия контрольных контейнеров не будет работать без сервисного местоположения. Это то, как они разрешают услуги внутри.
Но гораздо лучшим примером являются ASP.NET MVC и WebApi. Как вы думаете, что делает возможным внедрение зависимостей в контроллерах? Правильно - местоположение сервиса.
Ваши вопросы
Есть еще две серьезные проблемы:
С помощью конструктора с использованием контейнера вы получаете это бесплатно.
Это правда. Но с внедрением конструктора вам не нужно сканировать весь класс, чтобы выяснить, какие зависимости отсутствуют.
И некоторые лучшие контейнеры также проверяют все зависимости при запуске (путем сканирования всех конструкторов). Таким образом, с этими контейнерами вы получаете ошибку времени выполнения напрямую, а не в какой-то более поздний момент времени.
Нет. Поскольку у вас нет зависимости от статического сервисного локатора. Вы пытались заставить параллельные тесты работать со статическими зависимостями? Это не весело.
источник
Я также хотел бы отметить, что, если вы проводите рефакторинг устаревшего кода, шаблон Service Locator не только не является анти-шаблоном, но и практической необходимостью. Никто никогда не собирается махать волшебной палочкой за миллионы строк кода, и внезапно весь этот код будет готов к DI. Поэтому, если вы хотите начать вводить DI в существующую кодовую базу, часто бывает так, что вы будете медленно менять положение, чтобы стать сервисами DI, и код, который ссылается на эти сервисы, часто НЕ будет сервисами DI. Следовательно, ЭТИМ сервисам нужно будет использовать сервисный локатор, чтобы получить экземпляры тех сервисов, которые были преобразованы для использования DI.
Таким образом, при рефакторинге больших унаследованных приложений, чтобы начать использовать концепции DI, я бы сказал, что Service Locator не только НЕ является паттерном, но и является единственным способом постепенного применения концепций DI к базе кода.
источник
С точки зрения тестирования, Service Locator плохой. См. Хорошее объяснение Google Tech Talk Миско Хевери с примерами кода http://youtu.be/RlfLCWKxHJ0, начиная с минуты 8:45. Мне понравилась его аналогия: если вам нужно 25 долларов, просите деньги напрямую, а не отдавайте свой кошелек, откуда деньги будут взяты. Он также сравнивает сервисный локатор с стогом сена, в котором есть нужная игла, и знает, как ее извлечь. Классы, использующие Service Locator, трудно использовать из-за этого.
источник
Есть две разные причины, по которым использование сервисного локатора плохо в этом отношении.
Просто и понятно: класс с указателем службы в нем труднее использовать повторно, чем класс, который принимает свои зависимости через конструктор.
источник
Мои знания недостаточно хороши, чтобы судить об этом, но в целом, я думаю, что если что-то находит применение в конкретной ситуации, это не обязательно означает, что оно не может быть анти-паттерном. Особенно, когда вы имеете дело со сторонними библиотеками, у вас нет полного контроля над всеми аспектами, и вы можете в конечном итоге использовать не самое лучшее решение.
Вот параграф из Адаптивного кода через C # :
источник
Автор полагает, что «компилятор вам не поможет» - и это правда. Когда вы определяете класс, вам нужно тщательно выбирать его интерфейс - среди других целей, чтобы сделать его настолько независимым, насколько это ... имеет смысл.
Когда клиент принимает ссылку на службу (на зависимость) через явный интерфейс, вы
Вы правы, что у DI есть свои проблемы / недостатки, но упомянутые преимущества намного перевешивают их ... ИМО. Вы правы, что в DI есть зависимость, введенная в интерфейс (конструктор) - но, надеюсь, это та самая зависимость, которая вам нужна и которую вы хотите сделать видимой и проверяемой.
источник
Да, сервисный локатор является анти-паттерном, он нарушает инкапсуляцию и является солидным .
источник