Если синглтоны плохие, то почему сервисный контейнер хорош? - PullRequest
87 голосов
/ 17 мая 2011

Мы все знаем, как плохие синглеты , потому что они скрывают зависимости и по другим причинам .

Но в рамках может быть много объектов, которые должныбыть экземпляром только один раз и вызываться отовсюду (logger, db и т. д.).

Для решения этой проблемы мне сказали использовать так называемый "диспетчер объектов" (или ServiceКонтейнер , такой как symfony), внутри которого хранятся все ссылки на службы (регистратор и т. Д.).

Но почему поставщик услуг не так плох, как чистый синглтон?

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

PS.Я знаю, что для того, чтобы не скрывать зависимости, я должен использовать DI (как заявлено Миско)

Добавить

Я бы добавил: в наши дни синглеты не так уж и плохи, создатель PHPUnit объяснил это здесь:

DI +Синглтон решает проблему:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }

        // ...
    }
}
?>

это довольно умно, даже если это не решает всех проблем.

Кроме DI и Service Container есть ли какие-нибудь приемлемые решение для доступа к этим вспомогательным объектам?

Ответы [ 5 ]

75 голосов
/ 17 мая 2011

Сервисный локатор, так сказать, меньшее из двух зол.«Меньше» сводится к этим четырем различиям ( по крайней мере, я не могу думать ни о каких других сейчас ):

Принцип единой ответственности

Контейнер услуг ненарушать принцип единой ответственности, как синглтон.Singletons смешивает создание объектов и бизнес-логику, в то время как Service Container строго отвечает за управление жизненными циклами объектов вашего приложения.В этом отношении Service Container лучше.

Связь

Синглтоны обычно жестко запрограммированы в вашем приложении из-за статических вызовов методов, что приводит к сильной связи и труднодоступным зависимостям в вашем коде.SL с другой стороны - это всего лишь один класс, и его можно вводить.Так что, хотя все ваши классы будут зависеть от этого, по крайней мере, это слабо связанная зависимость.Поэтому, если вы не внедрили ServiceLocator как сам Singleton, это несколько лучше, а также проще для тестирования.

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

Скрытые зависимости

Однако проблема сокрытия зависимостей существует в значительной степени.Когда вы просто внедряете локатор в ваши потребляющие классы, вы не будете знать никаких зависимостей.Но в отличие от Singleton, SL обычно создает все зависимости, необходимые за кулисами.Поэтому, когда вы получаете Службу, вы не видите, как Misko Hevery в примере CreditCard , например, вам не нужно создавать экземпляры всех зависимостей вручную.

Извлекать зависимости извнутри экземпляра также нарушается Закон Деметры , который гласит, что вы не должны копаться в соавторах.Экземпляр должен общаться только со своими непосредственными сотрудниками.Это проблема как для Singleton, так и для ServiceLocator.

Global State

Проблема Global State также несколько смягчается, поскольку при создании экземпляра нового Service Locator между тестами удаляются все ранее созданные экземпляры.также (если вы не допустили ошибку и не сохранили их в статических атрибутах в SL).Конечно, это не относится к любому глобальному состоянию в классах, управляемых SL.

Также см. Фаулера в Локатор службы и Внедрение зависимостей для более углубленного обсуждения.


Примечание о вашем обновлении и связанная статья Себастьяна Бергмана о тестировании кода, использующего Singletons : Себастьян никоим образом не предполагает, что предложенный обходной путь делает использование Singleons менеепроблема.Это всего лишь один из способов сделать код, который в противном случае было бы невозможно проверить более тестируемым.Но это все еще проблемный код.На самом деле он прямо отмечает: «Только то, что ты можешь, не значит, что ты должен».

42 голосов
/ 17 мая 2011

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

Итак, ваш вопрос: почему сервисные локаторы хороши?Мой ответ: это не так.

Избегайте, избегайте, избегайте.

3 голосов
/ 17 мая 2011

Сервисный контейнер скрывает зависимости, как это делает шаблон Singleton.Возможно, вы захотите предложить вместо этого использовать контейнеры для внедрения зависимостей, поскольку у него есть все преимущества сервисного контейнера, но нет (насколько я знаю) недостатков, которые есть у сервисного контейнера.

Насколько я понимаю, единственныйРазница между ними заключается в том, что в служебном контейнере служебный контейнер - это вводимый объект (таким образом, скрывающий зависимости). Когда вы используете DIC, DIC вводит соответствующие зависимости для вас.Класс, управляемый DIC, совершенно не замечает тот факт, что он управляется DIC, поэтому у вас меньше связей, четких зависимостей и удачных юнит-тестов.

Это хороший вопрос в SO, объясняющий разницуиз обоих: В чем разница между шаблонами Dependency Injection и Service Locator?

1 голос
/ 17 мая 2011

Поскольку вы можете легко заменить объекты в контейнере службы на
1) наследование (класс Object Manager может быть унаследован, а методы могут быть переопределены)
2) изменение конфигурации (в случае с Symfony)

И, синглтоны плохие не только из-за сильной связи, но и потому, что они являются _ Single _tons. Это неправильная архитектура для почти всех видов объектов.

С «чистым» DI (в конструкторах) вы заплатите очень большую цену - все объекты должны быть созданы до того, как они будут переданы в конструктор. Это будет означать больше используемой памяти и меньшую производительность. Кроме того, не всегда объект может быть просто создан и передан в конструктор - может быть создана цепочка зависимостей ... Мой английский недостаточно хорош, чтобы полностью об этом поговорить, прочитайте об этом в документации Symfony.

0 голосов
/ 20 мая 2011

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

Например, у меня есть front-end и admin. Внутри администратора, я хочу, чтобы они могли войти в систему как пользователь. Рассмотрим код внутри admin.

$frontend = new Frontend();
$frontend->auth->login($_GET['user']);
$frontend->redirect('/');

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

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

$logger1=$api->add('Logger');
$logger2=$api->add('Logger');

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

Наконец, если вы хотите использовать объектно-ориентированную разработку, работайте с объектами, а не с классами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...