ServiceLocator-это анти-паттерн?



недавно я читал Марк Зееман-х о службе локатора анти-шаблон.



автор указывает на две основные причины, по которым ServiceLocator является анти-шаблоном:




  1. проблема использования API (что меня вполне устраивает)

    Когда класс использует локатор служб, очень трудно увидеть его зависимости, поскольку в большинстве случаев класс имеет только один конструктор без параметров.
    В отличие от ServiceLocator, DI подход явное представление зависимостей через параметры конструктора, поэтому зависимости легко увидеть в IntelliSense.


  2. проблема обслуживания (который ставит меня в тупик)

    Рассмотрим следующий пример



у нас есть класс 'MyType' который использует подход Service locator:



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();
}
}


а вот где мой начинается непонимание. Автор говорит:




становится намного труднее сказать, вводите ли вы критическое изменение или нет. Вам нужно понять все приложение, в котором используется Service Locator, и компилятор вам не поможет.




но подождите секунду, если бы мы использовали подход DI, Мы бы ввели зависимость с другим параметром в конструкторе (в случае инъекции конструктора). И проблема будет еще там. Если мы можем забыть настроить ServiceLocator, то мы можем забыть добавить новое сопоставление в наш контейнер IoC, и подход DI будет иметь ту же проблему во время выполнения.



кроме того, автор упомянул о трудностях модульного теста. Но разве у нас не будет проблем с подходом DI? Разве нам не нужно будет обновить все тесты, которые создавали этот класс? Мы обновим их, чтобы передать новую издевательскую зависимость, чтобы сделать наш тест компилируемым. И я не вижу никаких преимуществ от этого обновления и времени расходование.



Я не пытаюсь защитить подход Service Locator. Но это недоразумение заставляет меня думать, что я теряю что-то очень важное. Может кто-нибудь развеять мои сомнения?



ОБНОВЛЕНИЕ (РЕЗЮМЕ):



ответ на мой вопрос "Является ли Service Locator анти-шаблоном" действительно зависит от обстоятельств. И я определенно не предложил бы вычеркнуть его из вашего списка инструментов. Это может стать очень удобно, когда вы начинаете иметь дело с наследием код. Если вам посчастливилось быть в самом начале вашего проекта, то подход DI может быть лучшим выбором, поскольку он имеет некоторые преимущества перед Service Locator.



и вот основные отличия, которые убедили меня не использовать Service Locator для моих новых проектов:




  • самое очевидное и важное: Service Locator скрывает зависимости классов

  • если вы используете какой-то контейнер IoC, он, вероятно, будет сканировать весь конструктор при запуске, чтобы проверить все зависимости и дать вам немедленную обратную связь по отсутствующим сопоставлениям (или неправильной конфигурации); это невозможно, если вы используете свой контейнер IoC в качестве Service Locator


Подробнее читайте отличные ответы, которые приведены ниже.

674   6  

6 ответов:

Если вы определяете шаблоны как анти-шаблоны только потому, что есть некоторые ситуации, когда он не подходит, то да, это анти-шаблон. Но с этим рассуждением все паттерны также были бы анти-паттернами.

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

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();

        // new dependency
        var dep2 = Locator.Resolve<IDep2>();
        dep2.DoSomething();
    }
}

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

var myType = new MyType();
myType.MyMethod();

вы не понимаете, что он имеет зависимостей, если они скрыты с помощью сервисной точки. Теперь, если мы вместо этого используем инъекцию зависимостей:

public class MyType
{
    public MyType(IDep1 dep1, IDep2 dep2)
    {
    }

    public void MyMethod()
    {
        dep1.DoSomething();

        // new dependency
        dep2.DoSomething();
    }
}

вы можете непосредственно определить зависимости и не можете использовать классы до их удовлетворения.

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

является ли шаблон анти-шаблон?

нет.

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

но гораздо лучший пример ASP.NET MVC и WebApi. Как вы думаете, что делает инъекцию зависимостей возможной в контроллерах? Правильно - расположение службы.

вопросы

но подождите секунду, если бы мы использовали подход DI, Мы бы ввели зависимость с другим параметром в конструкторе (в случае инъекция конструктора). И проблема будет все еще там.

есть еще две серьезные проблемы:

  1. С расположением службы вы также добавляете еще одну зависимость: локатор служб.
  2. как вы определяете, какое время жизни должны иметь зависимости, и как / когда они должны быть очищены вверх?

С помощью инъекции конструктора с помощью контейнера вы получаете это бесплатно.

Если можно забудьте настроить ServiceLocator, тогда мы можем забыть добавить новый отображение в нашем контейнере IoC и подходе DI будет иметь то же самое проблема времени выполнения.

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

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

кроме того, автор упомянул о трудностях модульного теста. Но разве у нас не будет проблем с подходом DI?

нет. Поскольку у вас нет зависимости от статического локатора служб. Вы пробовали получить параллельные тесты, работающие со статическими зависимостями? Это не весело.

Я также хотел бы отметить, что если вы рефакторинг устаревшего кода, что шаблон Service Locator не только не является анти-шаблоном, но и является практической необходимостью. Никто никогда не взмахнет волшебной палочкой над миллионами строк кода, и вдруг весь этот код будет готов. Поэтому, если вы хотите начать вводить DI в существующую базу кода, часто бывает так, что вы будете медленно изменять вещи, чтобы стать службами DI, и код, который ссылается на эти службы часто не будет DI услуг. Следовательно, эти службы должны будут использовать локатор служб, чтобы получить экземпляры тех служб, которые были преобразованы в использование DI.

с точки зрения тестирования, Service Locator плох. См. Google Tech Talk Мишко Хевери хорошее объяснение с примерами кодаhttp://youtu.be/RlfLCWKxHJ0 начало в 8:45. Мне понравилась его аналогия: если вам нужно 25 долларов, попросите напрямую деньги, а не отдавайте свой кошелек, откуда деньги будут взяты. Он также сравнивает Service Locator со стогом сена, в котором есть игла, которая вам нужна, и знает, как ее получить. Классы, использующие Service Locator, трудно повторно использовать из-за оно.

проблема обслуживания (который ставит меня в тупик)

есть 2 разные причины, почему использование service locator плохо в этом отношении.

  1. в вашем примере вы жестко кодируете статическую ссылку на service locator в свой класс. Это плотно пар ваш класс непосредственно в сервис локатор, что в свою очередь означает он не будет работать без локатора службы. Кроме того, ваши модульные тесты (и любой другой, кто использует класс) также неявно зависят от локатора служб. Одна вещь, которая, казалось, осталась незамеченной здесь является то, что при использовании инъекции конструктора вам не нужен контейнер DI при модульном тестировании, что значительно упрощает ваши модульные тесты (и способность разработчиков понимать их). Это реализованное преимущество модульного тестирования, которое вы получаете от использования инъекции конструктора.
  2. почему конструктор Intellisense важен, люди здесь, кажется, полностью упустили суть. Класс пишется один раз, но он может использоваться в нескольких приложениях (то есть в нескольких конфигурациях DI). Со временем это приносит дивиденды, если вы можете посмотреть на определение конструктора, чтобы понять зависимости класса, а не смотреть на (Надеюсь, последнюю) документацию или, в противном случае, вернуться к исходному исходному коду (что может быть неудобно), чтобы определить, что зависимости класса. Класс с локатором служб обычно проще написать, но вы больше чем оплачиваете цену этого удобства в продолжающемся обслуживании проекта.

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

рассмотрим случай, когда вам нужно использовать службу от LibraryA что ее автор решила бы использовать ServiceLocatorA и услуги с LibraryB автор которого решил бы использовать ServiceLocatorB. У нас нет другого выбора, кроме использования 2 различных сервисных локаторов в нашем проекте. Сколько зависимостей нужно настроить-это игра в угадайку, если у нас нет хорошей документации, исходного кода или автора на быстром наборе. В противном случае нам может потребоваться использовать декомпилятор просто чтобы выяснить, каковы зависимости. Возможно, нам придется настроить 2 совершенно разных сервиса API локатора, и в зависимости от дизайна, может быть невозможно просто обернуть существующий контейнер DI. Возможно, вообще невозможно разделить один экземпляр зависимости между двумя библиотеками. Сложность проекта может быть еще более усугублена, если локаторы служб не находятся в тех же библиотеках, что и необходимые нам Службы - мы неявно перетаскиваем дополнительные ссылки на библиотеки в наш проект.

Теперь рассмотрим те же два услуги, выполненные с помощью инъекции конструктора. Добавьте ссылку на LibraryA. Добавьте ссылку на LibraryB. Предоставьте зависимости в вашей конфигурации DI (путем анализа того, что необходимо с помощью Intellisense). Сделанный.

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

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

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

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

вы правы, что DI имеет свои проблемы / недостатки, но упомянутые преимущества перевешивают их на сегодняшний день ... ММО. Вы правы, что с DI есть зависимость, введенная в интерфейс (конструктор) - но это, надеюсь, та самая зависимость, которая вам нужна и которую вы хотите сделать видимой и проверяемой.

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

вот абзац из Адаптивный Код На C#:

"к сожалению, локатор услуга иногда неизбежный анти-паттерн. В некоторых типах приложений - особенно Windows Workflow Foundation - инфраструктура не поддается внедрению конструктора. В этих случаях единственной альтернативой является использование сервис локатора. Это лучше, чем вообще не вводить зависимости. Для всего моего купороса против (анти-) шаблона это бесконечно лучше, чем вручную строить зависимости. В конце концов, он по-прежнему позволяет использовать все важные точки расширения, предоставляемые интерфейсами это позволяет декораторам, адаптерам и аналогичным преимуществам."

-- Холл, Гэри Маклин. Адаптивный код через C#: гибкое кодирование с шаблонами проектирования и твердыми принципами (Ссылка для разработчиков) (стр. 309). Образование Пирсона.

Comments

    Ничего не найдено.