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