Если синглтоны плохи, то почему контейнер обслуживания хорош?


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

но в рамках может быть много объектов, которые нужно создать только один раз и вызвать везде (регистратор, db etc).

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

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

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

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

добавить

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

DI + Singleton решает проблему:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

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

        // ...
    }
}
?>

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

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

5 79

5 ответов:

Service Locator - это просто меньшее из двух зол, так сказать. "Меньшее" сводится к этим четырем различиям (по крайней мере, я не могу думать о любых других прямо сейчас):

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

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

соединение

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

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

скрытые Зависимости

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

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

Глобальное Состояние

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

Также см. Фаулер на Service Locator vs Dependency Injection для гораздо более глубокого обсуждения.


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

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

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

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

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

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

Это хороший вопрос при таком объяснении разницы обоих: в чем разница между внедрением зависимостей и шаблонами локатора служб?

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

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

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

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

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

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

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

моя идея синглтона - вы не можете добавить один и тот же объект внутри родителя дважды. Например,

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

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

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