Является ли DI единственным решением для Singleton и / или статических объектов? - PullRequest
8 голосов
/ 17 мая 2011

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

Мне сказали, что статические методы / объекты тоже не годятся.

Таким образом, в основном единственным решением является внедрение зависимостей .

Но ... я действительно не могу привыкнуть к DI, возьмите этот пример:

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

С DI мой код будет превращаться в:

global $logger; //> consider i have been instanciated it at the start of my fw

$query = new PreparedQuery($logger);
$query->prepare() etc.

Теперь это не так уж и плохо. Но рассмотрим страницу, которая требует много запросов. Я считаю, что это довольно избыточно - каждый раз писать $logger в конструкторе, особенно если учесть, нужно ли PreparedQuery много других зависимостей в конструкторе.

Единственное решение для избегания синглтона, которое я нашел , заключается в использовании метода (или просто простой функции) в главном приложении, которое хранит все ссылки на объекты-помощники ( Service Locator / Container), но это не решает проблему сокрытия зависимостей

Итак, по вашему опыту, кроме DI, какой шаблон лучше использовать?

Решение:

Для всех, кто интересуется, создатель PHPunit объясняет, как решить проблему синглтона (и как решить проблему тестирования статических методов с помощью PHP 5.3)

Довольно интересно читать, если вы спросите меня.

Ответы [ 5 ]

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

Пожалуйста, не используйте global.
Вам нужно передать $ logger в конструкторах или вместо этого передать Service Container (также известный как Менеджер объектов, Сервисный локатор, Менеджер ресурсов).
Вариант от Symfony Framework http://symfony.com/doc/current/book/service_container.html
Вы можете создать свой собственный менеджер объектов, и его методы не должны быть статичными.

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

Ну, в этом случае я бы вместо этого построил строитель (или фабрику). Таким образом, ваша фабрика будет вводить зависимость для вас. Таким образом, вы также можете избежать глобальных изменений:

class PreparedQueryFactory {
    protected $logger = null;
    public function __construct($loggger) {
        $this->logger = $logger;
    }
    public function create() {
        return new PreparedQuery($this->logger);
    }
}

Таким образом, вы делаете один раз:

$factory = new PreparedQueryFactory($logger);

Тогда, когда вам понадобится новый запрос, просто позвоните:

$query = $factory->create();

Теперь это очень простой пример. Но вы можете добавить все виды сложной логики, если вам нужно. Но дело в том, что, избегая new в своем коде, вы также избегаете управления зависимостями. Таким образом, вместо этого вы можете передать фабрику (и) по мере необходимости.

Преимущество заключается в том, что все это на 100% поддается проверке, поскольку все вводится везде (в отличие от использования глобальных).

Вы также можете использовать реестр (также известный как Service Container или DI Container), но убедитесь, что вы вводите реестр в.

1 голос
/ 15 апреля 2012

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

Упрощенный пример:

class Logger implements Observer {
    public function notify($tellMeWhatHappened) {
         // oh really? let me do xyz
    }
}

class Query implements Observable {
    private $observers = array();

    public function addObserver(Observer $observer) {
        $this->observers[] = $observer;
    }

    public function foo() {
        // great code
        foreach ($this->observers as $observer) { $observer->notify('did not work'); }
    }
}

Это удаляет Logger от конструктора. Это то, что я предпочитаю, если это не важно для функционирования объекта.

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

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

0 голосов
/ 25 марта 2012

В моем понимании разговоров Миско Хевери о DI и операторе new проблема в том, что вы не продвинулись достаточно в реализации DI.

Хэвери всегда говорит, что вы не должны смешивать бизнес-логику с конструкцией объекта. Однако в двух строках вашего примера первая ($query = new PreparedQuery($logger);) создает объект, а затем вторая ($query->prepare(/* ... */);) - бизнес-логика.

Очевидно, что целью этого кода является подготовка запроса, и вместо того, чтобы беспокоиться о том, как построить PreparedQuery, он должен просто запросить его в конструкторе класса. Или, если ему нужно иметь возможность создавать множество PreparedQueries, ему следует запросить прототип (который он будет клонировать всякий раз, когда ему понадобится новый) или объект фабрики. Дело в том, что наличие в PreparedQuery логгера не имеет значения, и о нем нужно позаботиться.

Принцип «спросить, что вам нужно» в конструкторе легко понять в принципе, хотя я все еще пытаюсь понять для себя, что это означает на практике в различных ситуациях, и как реализовать его полностью наверх («основной метод» или эквивалент). Тем не менее, я думаю, что этот принцип говорит о вашей общей проблеме. Этот оператор new не должен быть там, где он находится.

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