Все мы знаем, насколько плохи синглтоны , потому что они скрывают зависимости и по другим причинам .
Но во фреймворке может быть много объектов, которые нужно создать только один раз и вызывать из любого места (регистратор, база данных и т. Д.).
Чтобы решить эту проблему, мне посоветовали использовать так называемый «диспетчер объектов» (или контейнер служб, например, symfony), который хранит внутри все ссылки на службы (регистратор и т.
Но почему поставщик услуг не так плох, как чистый синглтон?
Поставщик услуг также скрывает зависимости, и они просто завершают создание первой зависимости. Так что я действительно изо всех сил пытаюсь понять, почему мы должны использовать поставщика услуг вместо синглтонов.
PS. Я знаю, что чтобы не скрывать зависимости, я должен использовать DI (как заявил Миско)
Добавить
Я бы добавил: в наши дни синглтоны не такое уж зло, - объяснил создатель PHPUnit:
DI + Singleton решает проблему:
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
это довольно умно, даже если это не решает всех проблем.
Есть ли какое-либо хорошее приемлемое решение для доступа к этим вспомогательным объектам, кроме DI и Service Container ?
источник
Ответы:
Service Locator - это, так сказать, меньшее из двух зол. «Меньшее», сводящееся к этим четырем отличиям ( по крайней мере, я не могу сейчас вспомнить другие ):
Принцип единой ответственности
Сервисный контейнер не нарушает принцип единой ответственности, как это делает синглтон. Синглтоны смешивают создание объектов и бизнес-логику, в то время как Service Container строго отвечает за управление жизненными циклами объектов вашего приложения. В этом отношении Service Container лучше.
Связь
Синглтоны обычно жестко запрограммированы в вашем приложении из-за вызовов статических методов, что приводит к тесной связи и трудностям для имитации зависимостей. в вашем коде. С другой стороны, SL - это всего лишь один класс, и его можно вводить. Таким образом, хотя все ваши классы будут зависеть от него, по крайней мере, это слабосвязанная зависимость. Так что, если вы не реализовали ServiceLocator как сам синглтон, это несколько лучше и проще для тестирования.
Однако все классы, использующие ServiceLocator, теперь будут зависеть от ServiceLocator, который также является формой связывания. Это можно смягчить, используя интерфейс для ServiceLocator, поэтому вы не привязаны к конкретной реализации ServiceLocator, но ваши классы будут зависеть от существования какого-либо локатора, тогда как отсутствие использования ServiceLocator резко увеличивает повторное использование.
Скрытые зависимости
Однако проблема сокрытия зависимостей существует очень часто. Когда вы просто вводите локатор в свои классы потребления, вы не узнаете никаких зависимостей. Но в отличие от синглтона SL обычно создает экземпляры всех необходимых зависимостей за кулисами. Таким образом, когда вы загружаете Сервис, вы не в конечном итоге, как Миско Хевери в примере с CreditCard , например, вам не нужно создавать экземпляры всех зависимостей вручную.
Получение зависимостей из экземпляра также нарушает Закон Деметры , который гласит, что вы не должны копаться в соавторах. Экземпляр должен разговаривать только со своими непосредственными сотрудниками. Это проблема как синглтона, так и с ServiceLocator.
Глобальное состояние
Проблема глобального состояния также несколько смягчается, потому что, когда вы создаете экземпляр нового локатора службы между тестами, все ранее созданные экземпляры также удаляются (если вы не допустили ошибку и не сохранили их в статических атрибутах в SL). Конечно, это не относится ни к одному глобальному состоянию в классах, управляемых SL.
Также см. Фаулер « Локатор сервисов против внедрения зависимостей» для более подробного обсуждения.
Заметка о вашем обновлении и связанная статья Себастьяна Бергманна о тестировании кода, использующего синглтоны : Себастьян никоим образом не предполагает, что предлагаемый обходной путь делает использование синглтонов менее проблематичным. Это всего лишь один из способов сделать код, который иначе было бы невозможно протестировать более тестируемым. Но это все еще проблемный код. Фактически, он прямо отмечает: «Просто потому, что ты можешь, это не значит, что ты должен».
источник
Шаблон локатора сервисов - это анти-шаблон. Это не решает проблему выявления зависимостей (вы не можете сказать, глядя на определение класса, каковы его зависимости, потому что они не внедряются, вместо этого они выдергиваются из локатора службы).
Итак, ваш вопрос: чем хороши локаторы сервисов? Мой ответ: это не так.
Избегайте, избегайте, избегайте.
источник
Контейнер службы скрывает зависимости, как это делает шаблон Singleton. Вы можете предложить вместо этого использовать контейнеры для внедрения зависимостей, поскольку у него есть все преимущества контейнера службы, но нет (насколько мне известно) недостатков, которые есть у контейнера службы.
Насколько я понимаю, единственная разница между ними заключается в том, что в контейнере службы контейнер службы - это внедряемый объект (таким образом скрывая зависимости), когда вы используете DIC, DIC вводит для вас соответствующие зависимости. Класс, которым управляет DIC, полностью игнорирует тот факт, что им управляет DIC, поэтому у вас меньше связей, четких зависимостей и удачных модульных тестов.
Это хороший вопрос для SO, объясняющий разницу между ними: в чем разница между шаблонами внедрения зависимостей и локатора служб?
источник
Потому что вы можете легко заменить объекты в Service Container с помощью
1) наследования (класс диспетчера объектов может быть унаследован, а методы могут быть переопределены)
2) изменения конфигурации (в случае с Symfony)
И синглтоны плохи не только из-за высокой связи, но и потому, что они _ одиночные _ton. Это неправильная архитектура почти для всех видов объектов.
С «чистым» DI (в конструкторах) вы заплатите очень большую цену - все объекты должны быть созданы до того, как будут переданы в конструктор. Это будет означать больше используемой памяти и меньшую производительность. Кроме того, не всегда объект можно просто создать и передать в конструктор - можно создать цепочку зависимостей ... Мой английский недостаточно хорош, чтобы обсуждать это полностью, читайте об этом в документации Symfony.
источник
Лично я стараюсь избегать глобальных констант, синглтонов по простой причине: бывают случаи, когда мне может понадобиться запустить API.
Например, у меня есть фронтенд и админ. Внутри администратора я хочу, чтобы они могли войти в систему как пользователь. Рассмотрим код внутри админки.
$frontend = new Frontend(); $frontend->auth->login($_GET['user']); $frontend->redirect('/');
Это может установить новое соединение с базой данных, новый регистратор и т. Д. Для инициализации внешнего интерфейса и проверить, действительно ли пользователь существует, действителен и т. Д. Он также будет использовать соответствующие отдельные файлы cookie и службы определения местоположения.
Моя идея синглтона - вы не можете добавить один и тот же объект внутри родителя дважды. Например
$logger1=$api->add('Logger'); $logger2=$api->add('Logger');
оставит вас с одним экземпляром и обеими переменными, указывающими на него.
Наконец, если вы хотите использовать объектно-ориентированную разработку, работайте с объектами, а не с классами.
источник
$api
var вокруг вашей структуры? Я не совсем понял, что вы имеете в виду. Также, если вызовadd('Logger')
возвращает тот же экземпляр, в основном у вас есть сервисный помощник