Я понимаю, что непосредственное создание зависимостей внутри класса считается плохой практикой. Это имеет смысл, поскольку это тесно связывает все, что в свою очередь делает тестирование очень трудным.
Похоже, что почти все фреймворки, с которыми я сталкивался, предпочитают внедрение зависимостей с контейнером, а не использование сервисных локаторов. Кажется, что они оба достигают одного и того же, позволяя программисту указать, какой объект должен быть возвращен, когда классу требуется зависимость.
В чем разница между двумя? Почему я бы выбрал одно над другим?
Ответы:
Когда сам объект отвечает за запрос своих зависимостей, а не принимает их через конструктор, он скрывает некоторую важную информацию. Это только немного лучше, чем очень тесно связанный случай использования
new
для создания своих зависимостей. Это уменьшает связь, потому что вы можете фактически изменить зависимости, которые он получает, но у него все еще есть зависимость, которую он не может поколебать: локатор службы. Это становится вещью, от которой все зависит.Контейнер, который предоставляет зависимости через аргументы конструктора, дает наибольшую ясность. Мы сразу видим, что объект нуждается как в
AccountRepository
, так и вPasswordStrengthEvaluator
. При использовании сервисного локатора эта информация менее очевидна. Вы бы сразу увидели случай, когда объект имеет, о-о, 17 зависимостей, и сказали бы себе: «Хм, это похоже на многое. Что там происходит?» Обращения к локатору службы могут распространяться вокруг различных методов и скрываться за условной логикой, и вы можете не осознавать, что создали «класс Бога» - тот, который делает все. Возможно, этот класс можно было бы реорганизовать в 3 небольших класса, которые были бы более сфокусированными и, следовательно, более тестируемыми.Теперь рассмотрим тестирование. Если объект использует локатор службы для получения своих зависимостей, вашей тестовой среде также понадобится локатор службы. В тесте вы сконфигурируете локатор службы для предоставления зависимостей тестируемому объекту - возможно, a
FakeAccountRepository
и aVeryForgivingPasswordStrengthEvaluator
, а затем запустите тест. Но это больше работы, чем указание зависимостей в конструкторе объекта. И ваш тестовый фреймворк также становится зависимым от локатора сервиса. Это еще одна вещь, которую вы должны настроить в каждом тесте, что делает написание тестов менее привлекательным.Посмотрите на статью "Serivce Locator - Anti-Pattern" для статьи Марка Симана об этом. Если ты в мире .Net, возьми его книгу. Это очень хорошо.
источник
constructor supplied dependencies
vs,service locator
это то, что первая может быть проверена во время компиляции, а вторая может быть проверена только во время выполнения.But that's more work than specifying dependencies in the object's constructor.
я бы хотел возразить. С локатором сервиса вам нужно только указать 3 зависимости, которые вам действительно нужны для вашего теста. При использовании DI на основе конструктора необходимо указать ВСЕ 10 из них, даже если 7 не используются.Представьте, что вы работаете на фабрике по производству обуви .
Вы несете ответственность за сборку обуви, и для этого вам понадобится много вещей.
И так далее.
Ты на работе на фабрике и готов начать. У вас есть список инструкций о том, как действовать, но у вас еще нет материалов или инструментов.
Service Locator подобен Формана , который может помочь вам получить то , что вам нужно.
Вы спрашиваете Сервисный Локатор каждый раз, когда вам что-то нужно, и они уходят, чтобы найти это для вас. Локатору Сервиса заранее сообщили о том, что вы, вероятно, попросите и как его найти.
Вам лучше надеяться, что вы не попросите чего-то неожиданного. Если локатор не был заранее проинформирован о конкретном инструменте или материале, он не сможет получить его для вас и просто пожмет вам плечами.
Dependency Injection (DI) Контейнер как большой ящик , который наполняется все , что каждый нуждается в начале дня.
Когда завод запускается, Большой Босс, известный как Корень Композиции, берет контейнер и раздает все Линейным Менеджерам .
Теперь у линейных менеджеров есть то, что им нужно для выполнения своих обязанностей в течение дня. Они берут то, что имеют, и передают то, что нужно, своим подчиненным.
Этот процесс продолжается, с зависимостями, стекающими по линии производства. В конце концов контейнер с материалами и инструментами появляется для вашего мастера.
Теперь ваш мастер распределяет именно то, что вам нужно, вам и другим работникам, даже не спрашивая их.
По сути, как только вы приходите на работу, все, что вам нужно, уже в ящике и ждет вас. Вам не нужно ничего знать о том, как их получить.
источник
Несколько дополнительных очков, которые я нашел, прочесывая сеть:
источник
Я опаздываю на эту вечеринку, но не могу удержаться.
Иногда нет вообще. Разница в том, что знает о чем.
Вы знаете, что используете локатор службы, когда клиент, ищущий зависимость, знает о контейнере. Клиент, знающий, как найти свои зависимости, даже проходя через контейнер для их получения, является шаблоном поиска сервисов.
Означает ли это, что если вы хотите избежать поиска сервисов, вы не можете использовать контейнер? Нет. Вы просто должны помешать клиентам узнать о контейнере. Главное отличие в том, где вы используете контейнер.
Скажем,
Client
нуждыDependency
. Контейнер имеетDependency
.Мы только что следовали шаблону локатора службы, потому что
Client
знают, как найтиDependency
. Конечно, он использует жестко запрограммированный код,ClassPathXmlApplicationContext
но даже если вы введете, что у вас все еще есть сервисный локатор из-заClient
вызововbeanfactory.getBean()
.Чтобы избежать поиска сервисов, вам не нужно отказываться от этого контейнера. Вы просто должны убрать это,
Client
такClient
что не знаете об этом.Обратите внимание, как
Client
теперь не знает, что контейнер существует:Переместите контейнер из всех клиентов и вставьте его в main, где он может построить граф объектов из всех ваших долгоживущих объектов. Выберите один из этих объектов для извлечения и вызовите метод для него, и вы начнете тикать весь график.
Это перемещает всю статическую конструкцию в контейнеры XML, но в то же время держит всех ваших клиентов в блаженном неведении относительно того, как найти их зависимости.
Но главный все еще знает, как найти зависимости! Да. Но не распространяя эти знания вокруг, вы избежали основной проблемы локатора сервисов. Решение об использовании контейнера теперь принимается в одном месте и может быть изменено без переписывания сотен клиентов.
источник
Я думаю, что самый простой способ понять разницу между ними и почему DI-контейнер намного лучше, чем локатор служб, это подумать, почему мы вообще делаем инверсию зависимостей.
Мы делаем инверсию зависимостей, чтобы каждый класс явно указывал, от чего он зависит для работы. Мы делаем это, потому что это создает самую слабую связь, которую мы можем достичь. Чем слабее связь, тем проще что-то тестировать и реорганизовывать (и, как правило, требует наименьшего рефакторинга в будущем, потому что код чище).
Давайте посмотрим на следующий класс:
В этом классе мы явно заявляем, что нам нужен IOutputProvider и ничего больше, чтобы этот класс работал. Это полностью тестируется и зависит от одного интерфейса. Я могу переместить этот класс в любое место моего приложения, включая другой проект, и все, что ему нужно, - это доступ к интерфейсу IOutputProvider. Если другие разработчики хотят добавить что-то новое в этот класс, что требует второй зависимости, они должны четко указать, что им нужно в конструкторе.
Взгляните на тот же класс с помощью сервисного локатора:
Теперь я добавил сервисный локатор в качестве зависимости. Вот проблемы, которые сразу очевидны:
Так почему бы нам не сделать локатор службы статическим классом? Давайте взглянем:
Это намного проще, верно?
Неправильно.
Допустим, IOutputProvider реализован очень долго работающим веб-сервисом, который записывает строку в пятнадцать различных баз данных по всему миру и занимает очень много времени.
Давайте попробуем проверить этот класс. Нам нужна другая реализация IOutputProvider для теста. Как мы пишем тест?
Чтобы сделать это, нам нужно сделать некоторую необычную конфигурацию в статическом классе ServiceLocator, чтобы использовать другую реализацию IOutputProvider, когда он вызывается тестом. Даже писать это предложение было больно. Осуществление этого было бы мучительным, и это был бы кошмар обслуживания . Мы никогда не должны изменять класс специально для тестирования, особенно если этот класс не тот класс, который мы на самом деле пытаемся протестировать.
Итак, теперь у вас есть: а) тест, вызывающий навязчивые изменения кода в несвязанном классе ServiceLocator; или б) вообще никакого теста. И у вас остается менее гибкое решение.
Таким образом, класс локатора службы должен быть введен в конструктор. Это означает, что мы остались с конкретными проблемами, упомянутыми ранее. Локатор службы требует больше кода, сообщает другим разработчикам, что ему нужны вещи, которых нет, поощряет других разработчиков писать худший код и дает нам меньшую гибкость в продвижении вперед.
Проще говоря, сервисные локаторы увеличивают связность в приложении и побуждают других разработчиков писать высокосвязанный код .
источник