Недавно я прочитал статью Марка Симанна об анти-паттерне 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 в качестве локатора службы
Для подробностей читайте отличные ответы, которые приведены ниже.