design-patterns пример java - Является ли ServiceLocator анти-шаблоном?




3 Answers

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

Вместо этого нам нужно посмотреть, имеются ли правильные способы использования шаблонов, а для 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?

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

vs dependency

Недавно я прочитал статью Марка Семанна об анти-шаблоне Service Locator.

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

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

  2. Проблема с обслуживанием (что меня озадачивает)
    Рассмотрим следующий пример

У нас есть класс «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();
    }
}

И здесь начинается мое недоразумение. Автор говорит:

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

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

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

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

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

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

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

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

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




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




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

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

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

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




Related