Как правильно использовать компонент EventDispatcher Symfony? - PullRequest
9 голосов
/ 19 марта 2012

Я бы хотел способствовать слабой связи в своем PHP-коде, сделав некоторые классы наблюдаемыми. Компонент Symfony EventDispatcher выглядит многообещающе, как и SPL SplObserver / SplSubject пара классов.

Какой лучший способ сделать это? Я вижу несколько разных возможностей:

(1) Внедрить экземпляр EventDispatcher в каждый наблюдаемый класс (отслеживая глобальный экземпляр EventDispatcher):

class Foo
{
    public function __construct($dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function bar()
    {
        $this->dispatcher->dispatch(...);
    }
}

(2) Пусть наблюдаемый класс расширяет класс EventDispatcher:

class Foo extends EventDispatcher
{
    public function bar()
    {
        $this->dispatch(...);
    }
}

(3) Использовать SplObserver / SplSubject - просто, но не так гибко, как компонент EventDispatcher

Ответы [ 2 ]

48 голосов
/ 20 марта 2012

Отказ от ответственности: этот ответ не имеет ничего общего с Symfony EventDispatcher, но все, что связано с вашим вопросом.Если вы просто хотите получить ответ, вы можете пропустить (несколько) академическую дискуссию и перейти к концу.

Обсуждение

FACT : расширение приложенияРазмер означает конгруэнтное увеличение сложности.

С расширением области применения вашего приложения вы добавляете все больше и больше классов для реализации необходимой функциональности.Внезапно не так легко вспомнить, что объект Foo должен выполнять какое-то определенное действие при создании объекта Bar.Кроме того, когда ваши объекты начинают предоставлять друг другу дополнительные функциональные возможности, становится все сложнее поддерживать необходимые отношения, не заканчивая очень тесно связанными объектами.

Нам нужен способ взаимодействия объектов без явного жесткого кодированияссылки, которые мы забудем изменить, когда что-то изменится.Итак, как нам управлять такими взаимосвязанными функциями между узлами быстро растущего графа объектов?

Если вы хотите, чтобы это продолжалось, вы должны поговорить

Давайте сделаем небольшой обходмомент и рассмотрим романтическую метафору ...

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

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

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

Ввод управления событиями

И это все, что связано с управлением событиями.Цель состоит в том, чтобы предоставить нашим объектам средства для общения друг с другом таким образом, чтобы они не жестко кодировали отношения с объектами, с которыми они должны общаться.Как и в большинстве задач программирования, не существует единственного, конкретного, «правильного» способа сделать это.В вашем вопросе конкретно упоминаются два из доступных методов для достижения этой цели, поэтому я рассмотрю их.Если вы хотите узнать о некоторых других опциях, @ircmaxell недавно опубликовал хороший пост в блоге о том, как сделать PHP-приложения «подключаемыми» .

Observer

На практикевы найдете несколько реальных PHP-приложений для шаблона Observer.Это потому, что если вы хотите, чтобы ваш код был очень динамичным, вам не понадобится много времени, прежде чем вы присоедините наблюдателей к объектам повсюду.

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

Наше приложение было бы гораздо более гибким, если бы мы использовали централизованный диспетчер для уведомления заинтересованных объектов при возникновении события, хотя шаблон Observer может быть идеальным для одноразовых или простых случаев.

Посредник

Более надежным способом управления событиями является вставка централизованного слоя для обработки событий диспетчеризации соответствующим слушателям.Вот что делает шаблон Mediator wiki (и диспетчер событий Symfony).

Суть Mediator в том, что он централизовантранзитная станция для каждого события в системе, поэтому она должна быть доступна во всей области приложения (или в любом случае через опосредованные части).Обратите внимание, что не означает, что вы должны обращаться с ней как с глобальной переменной и обращаться к Mediator произвольно с глобальными ключевыми словами или заключать ее в некий злой одноэлементный объект или статическое свойство / метод.Такого рода злоупотребления приведут к проблемам, которые @liquorvicar изложил в первом ответе.Однако я категорически не согласен с оценкой этого ответа:

"Наличие EventDispatcher, который есть везде в вашем приложении и делает практически все, может усложнить тестирование / понимание / сопровождение вашего кода и т. Д. (Он может приблизиться кБожий объект) "

Это только в том случае, если вы неправильно используете Посредника;он должен отправлять уведомления о событиях и ничего больше.По этой причине я бы предостерег вас от его расширения, как вы предлагаете в варианте (2) вашего вопроса.При правильном использовании объект-посредник является чрезвычайно тестируемым.Нет ничего проще, чем издеваться над поведением объектов зависимости, указанных в ваших конструкторах.Вот что такое модульное тестирование.

Ответ

Итак, если вам нужно нелинейное управление событиями в вашем приложении, я настоятельно рекомендую вариант (1) изваш вопрос.Это вполне приемлемо, если вы не злоупотребляете этим.Похоже, что реализация Symfony поддерживает любой PHP, вызываемый как слушатель.Лично я предпочитаю систему, которая позволяет ленивую реализацию прослушивателей на основе классов для более эффективной и объектно-ориентированной парадигмы, но детали реализации остаются за вами.

Шаблон цепочки ответственности тесно связан сПосредник и является еще одним действительным методом для достижения аналогичных результатов.Если вам интересно, я бы предложил опубликованную ранее ссылку на пост в блоге @ ircmaxell.

2 голосов
/ 19 марта 2012

Я бы избегал (2).Наследование, вероятно, является наиболее часто используемым шаблоном и, вероятно, здесь не имеет значения.Выбор между вариантами (1) и (3), вероятно, зависит от вашего контекста.Хотя избегать жесткой связи - это хорошо, вы должны быть осторожны с решениями для швейцарских ножей.Наличие eventDispatcher, который есть везде в вашем приложении и делает почти все, может усложнить тестирование / понимание / сопровождение вашего кода и т. Д. (Он может приблизиться к объекту God).С другой стороны, решение Spl намного проще, и поэтому, если вам нужно несколько наблюдателей / наблюдаемых, вы можете обнаружить, что вам приходится поддерживать слишком много SplObservers / SplSubjects.

Как и в большинстве вещей в ООП, естьнет лучший способ, и, как правило, зависит от ваших конкретных случаев использования ...

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