Подтвердить что ты не робот

PHP: Я смешиваю программирование, управляемое событиями, с сигнальными интерфейсами (сигналом и слотом/шаблоном наблюдателя)?

Я видел много людей, говорящих, что Symfony2, Zend Framework 2 и другие управляются событиями.

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

Так как PHP-приложения не имеют никакого отношения к этому, нет никакого способа сделать такую ​​вещь. И.Е. Наблюдатели привязаны к просмотру, наблюдая изменения, когда пользователь использует интерфейс. Вместо этого для обновления представления требуется новый процесс запроса. Итак, это не событие, а целый новый запрос.

С другой стороны, существует аналогичная концепция: управляемая событиями архитектура.

Здесь вы можете прочитать оба:

http://en.wikipedia.org/wiki/Event-driven_programming

http://en.wikipedia.org/wiki/Event-driven_architecture

И вот другой:

http://en.wikipedia.org/wiki/Signal_programming

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

  • Описание тэга Stackoverflow [singals]

Кроме того, то, что я использовал для вызова event-driven, похоже, больше связано с шаблоном Signals and Slots, представленным Qt (реализация шаблона наблюдателя)

В качестве примера, существует Prado Framework, который утверждает, что управляется событиями:

http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Applications (раздел Application Lifecycles)

http://www.pradosoft.com/docs/manual/System/TApplication.html#methodonEndRequest

IIRC, это не приложение, управляемое событиями, а вместо этого просто плагины (сигналы и слоты), используемые классами, реализующими observable Interface. Я имею в виду, учитывая, как настольные приложения используют события и как приложения без учета состояния используют события (как плагины): первые используют события для всего приложения, включая представления, последние только для операций на стороне сервера.

Один из них больше связан с аспектно-ориентированным программированием (с сигналами и слотами), а другой не привязан к сквозным проблемам/AOP. Другими словами, это больше связано с состоянием приложения.

Итак, какова на самом деле связь между этими терминами и как они отличаются друг от друга?

  • Программирование, управляемое событиями
  • Архитектура, управляемая событиями
  • Шаблоны сигналов и слотов

Являются ли эти термины просто универсальными шаблонами? Следовательно, все, что реализует шаблон наблюдателя, можно считать управляемым событиями?

UPDATE

Zend Framework 2

Статья об АОП, которую я связал выше (http://mwop.net/blog/251-Aspects,-Filters,-and-Signals,-Oh,-My!.html) был написан Мэтью Вейером О'Финни (ZF Leader). IIRC, это не упоминают о "управляемом событиями", просто сигнале и слотах.

Symfony 2

В описании компонента Symfony2 EventDispatcher нет упоминаний о для приложений, управляемых событиями: http://symfony.com/doc/current/components/event_dispatcher/introduction.htmlОн содержит только ссылки на "События" (которые, собственно, обрабатываются сигналом и слотами).

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

4b9b3361

Ответ 1

Отказ от ответственности: Это длинный ответ, но я думаю, что стоит прочитать все его ссылки. И ИМХО приводит к окончательному ответу.

Я боролся с этими темами последние пару дней, и, если бы я все правильно прочитал, ответ:

Event-driven! == Запрос-ориентированный

"[...] Я нахожу это наиболее интересное различие в событии сотрудничество, перефразировать Jon Udell: программное обеспечение, управляемое запросами когда говорят, управляемое событиями программное обеспечение говорит, когда есть что сказать.

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

Мартин Фаулер - Коллаборация событий (раздел запросов)

Основываясь на этом утверждении, IIRC, современные фреймворки PHP реализуют шаблон Observer + перехватывающие фильтры + Singal и Slots, чтобы инициировать некоторые события во время цикла запроса.

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

Мы используем деление программ на несколько компонентов, которые сотрудничать вместе. (Я использую неопределенное слово "компонент" здесь сознательно, поскольку в этом контексте я имею в виду много вещей: в том числе объектов внутри программы и нескольких процессов, взаимодействующих через сети.) Наиболее распространенный способ заставить их сотрудничать - это стиль запроса/ответа. Если объект клиента хочет получить некоторые данные из salesman object, он вызывает метод на объекте продавца, чтобы спросить его для этих данных.

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

Мартин Фаулер - Фокус на события (раздел: Использование событий для сотрудничать)

Я думаю, что приложения PHP более тесно управляются событиями, чем запрос , только когда фокус находится на событиях. Если эти приложения/фреймворки используют события только для сквозных задач (АОП), то это не связано с событиями. Точно так же вы не будете называть его управляемым с помощью теста или управляемым доменом только потому, что у вас есть некоторые объекты домена и модульные тесты.

Примеры реального мира

Я выбрал несколько примеров, чтобы показать, почему эти фреймворки не полностью управляются событиями. Несмотря на события AOP, все управляются запросами:

Примечание. Хотя он может быть адаптирован к событиям

Zend Framework 2

Изучите компонент \Zend\Mvc\Application:

Он реализует \Zend\EventManager\EventManagerAwareInterface и полагается на \Zend\Mvc\MvcEvent, который описывает возможные события:

class MvcEvent extends Event
{
    /**#@+
     * Mvc events triggered by eventmanager
     */
    const EVENT_BOOTSTRAP      = 'bootstrap';
    const EVENT_DISPATCH       = 'dispatch';
    const EVENT_DISPATCH_ERROR = 'dispatch.error';
    const EVENT_FINISH         = 'finish';
    const EVENT_RENDER         = 'render';
    const EVENT_ROUTE          = 'route';

    // [...]
}

\Zend\Mvc\Application сам по себе управляется событиями, потому что он не сообщает напрямую с другими компонентами, а вместо этого, он просто запускает события:

/**
 * Run the application
 *
 * @triggers route(MvcEvent)
 *           Routes the request, and sets the RouteMatch object in the event.
 * @triggers dispatch(MvcEvent)
 *           Dispatches a request, using the discovered RouteMatch and
 *           provided request.
 * @triggers dispatch.error(MvcEvent)
 *           On errors (controller not found, action not supported, etc.),
 *           populates the event with information about the error type,
 *           discovered controller, and controller class (if known).
 *           Typically, a handler should return a populated Response object
 *           that can be returned immediately.
 * @return ResponseInterface
 */
public function run()
{
    $events = $this->getEventManager();
    $event  = $this->getMvcEvent();

    // Define callback used to determine whether or not to short-circuit
    $shortCircuit = function ($r) use ($event) {
        if ($r instanceof ResponseInterface) {
            return true;
        }
        if ($event->getError()) {
            return true;
        }
        return false;
    };

    // Trigger route event
    $result = $events->trigger(MvcEvent::EVENT_ROUTE, $event, $shortCircuit);
    if ($result->stopped()) {
        $response = $result->last();
        if ($response instanceof ResponseInterface) {
            $event->setTarget($this);
            $events->trigger(MvcEvent::EVENT_FINISH, $event);
            return $response;
        }
        if ($event->getError()) {
            return $this->completeRequest($event);
        }
        return $event->getResponse();
    }
    if ($event->getError()) {
        return $this->completeRequest($event);
    }

    // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

    // Complete response
    $response = $result->last();
    if ($response instanceof ResponseInterface) {
        $event->setTarget($this);
        $events->trigger(MvcEvent::EVENT_FINISH, $event);
        return $response;
    }

    $response = $this->getResponse();
    $event->setResponse($response);

    return $this->completeRequest($event);
}

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

Но есть одна важная вещь, которую нужно отметить: Это уровень представления (Controller + View). Доменный слой действительно может управляться событиями, но это не тот случай, когда почти все приложения вы видите там. ** Там есть смесь между управляемыми событиями и управляемыми запросами:

// albums controller
public function indexAction()
{
    return new ViewModel(array(
        'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
    ));
}

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

Symfony 2

Теперь рассмотрим то же самое на Symfony2 Application/FrontController: \Symfony\Component\HttpKernel\HttpKernel

На самом деле у него есть основные события во время запроса: Symfony\Component\HttpKernel\KernelEvents

/**
 * Handles a request to convert it to a response.
 *
 * Exceptions are not caught.
 *
 * @param Request $request A Request instance
 * @param integer $type    The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
 *
 * @return Response A Response instance
 *
 * @throws \LogicException If one of the listener does not behave as expected
 * @throws NotFoundHttpException When controller cannot be found
 */
private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
    // request
    $event = new GetResponseEvent($this, $request, $type);
    $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);

    if ($event->hasResponse()) {
        return $this->filterResponse($event->getResponse(), $request, $type);
    }

    // load controller
    if (false === $controller = $this->resolver->getController($request)) {
        throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
    }

    $event = new FilterControllerEvent($this, $controller, $request, $type);
    $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
    $controller = $event->getController();

    // controller arguments
    $arguments = $this->resolver->getArguments($request, $controller);

    // call controller
    $response = call_user_func_array($controller, $arguments);

    // view
    if (!$response instanceof Response) {
        $event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
        $this->dispatcher->dispatch(KernelEvents::VIEW, $event);

        if ($event->hasResponse()) {
            $response = $event->getResponse();
        }

        if (!$response instanceof Response) {
            $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));

            // the user may have forgotten to return something
            if (null === $response) {
                $msg .= ' Did you forget to add a return statement somewhere in your controller?';
            }
            throw new \LogicException($msg);
        }
    }

    return $this->filterResponse($response, $request, $type);
}

Но помимо того, что он "способен к событиям", он напрямую связывается с компонентом ControllerResolver, поэтому он не полностью управляется событиями с начала процесса запроса, хотя он вызывает некоторые события и позволяет некоторым компонентам быть подключаемыми (что не в случае ControllerResolver, который вводится как параметр конструктора).

Вместо этого, чтобы быть полностью управляемым событиями компонентом, он должен быть как в компоненте Application ZF2:

    // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

Prado

У меня недостаточно времени для изучения исходного кода, но сначала он не создается в SOLID путь. В любом случае, что контроллер на MVC-подобных платформах, Prado называет его TPage (еще не уверен):

http://www.pradosoft.com/demos/blog-tutorial/?page=Day3.CreateNewUser

И он действительно напрямую связывается с компонентами:

class NewUser extends TPage
{
    /**
     * Checks whether the username exists in the database.
     * This method responds to the OnServerValidate event of username custom validator.
     * @param mixed event sender
     * @param mixed event parameter
     */
    public function checkUsername($sender,$param)
    {
        // valid if the username is not found in the database
        $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
    }
    [...]
}

Я понимаю, что TPage является прослушивателем событий и может быть подключаемым. Но это не приводит к тому, что ваша модель домена управляется событиями. Поэтому я думаю, что в некоторой степени это ближе к предложению ZF2.

Примеры, управляемые событиями

Чтобы закончить этот длинный ответ, вот что будет иметь полномасштабное приложение, управляемое событиями:

Развязка приложений с доменами http://www.whitewashing.de/2012/08/25/decoupling_applications_with_domain_events.html

Поиск источников событий http://martinfowler.com/eaaDev/EventSourcing.html

Шаблон событий домена http://martinfowler.com/eaaDev/DomainEvent.html

Сотрудничество с событиями http://martinfowler.com/eaaDev/EventCollaboration.html

Перехват событий http://martinfowler.com/bliki/EventInterception.html

Конечная точка сообщения http://www.enterpriseintegrationpatterns.com/MessageEndpoint.html

... и т.д.

Ответ 2

PHP не является апатридом, HTTP - это. Чтобы сказать это просто, мы по существу построили слой поверх технологии без состояния, на которой мы можем реализовать проекты с сохранением состояния. В совокупности PHP и ваш хранилище данных имеют все инструменты, необходимые для создания дизайна приложения на основе шаблонов, управляемых событиями, посредством токенизации сеансов.

В очень обобщенном виде вы можете думать о том, что HTTP является для Интернета тем, что было для настольных компьютеров. На самом деле, сделайте это чуть чуть-чуть, и вы можете легко увидеть неявный, управляемый событиями характер сети. Вы сказали: "Это не событие, а целый новый запрос", и я возвращаюсь с "совершенно новым запросом - это событие", и я имею в виду это в смысле шаблона дизайна. Он имеет конкретное смысловое значение, связанное с вашим взаимодействием с вашим приложением.

По существу, с помощью таких шаблонов, как MVC и Front Controller (а также механизма HTTP файлов cookie и сеансов PHP), мы просто восстанавливаем состояние сеанса, а затем реагируем на событие, изменяя это состояние соответственно.

Мне нравится рассматривать суть REST: State State State Transfer... но я бы добавил, что мы не должны забывать о неявной импликации, что состояние передается только при возникновении события пользовательского интерфейса. Таким образом, мы поддерживаем контракты с HTTP, которые мы "говорим" только в "Представлениях" нашей модели (т.е. Документ, JSON и т.д.), Но это только наш диалект. Другие системы предпочитают говорить в координатах холста, сигнале db и т.д.

изменить/больше мыслей

Итак, я размышлял об этом некоторое время, и я думаю, что существует концепция, которая иллюстрирует немного двусмысленности при обсуждении этих шаблонов в области PHP через HTTP: детерминизм. В частности, как только запрос будет получен, путь выполнения PHP является детерминированным, и именно поэтому очень сложно рассматривать "управляемую событиями" архитектуру в PHP. Мое понятие состоит в том, что мы должны рассматривать один уровень выше, чем PHP, к более крупному "сеансу" взаимодействия с пользователем.

В настольных компьютерах мы используем runloops и state-ful context для "ожидания" событий. Тем не менее, я буду утверждать, что на самом деле Интернет - это улучшение по сравнению с этой архитектурой (в большинстве случаев), но в конечном итоге тот же шаблон. Вместо состояния runloop и бесконечной продолжительности мы загружаем наше состояние, когда происходит событие, а затем обрабатываем это событие. И вместо того, чтобы просто сохранять это состояние в памяти и ждать следующего события, мы архивируем это состояние и закрываем ресурсы. Его можно считать менее эффективным в одном смысле (нам нужно загружать состояние на каждом "событии" ), но его также можно назвать более эффективным, поскольку в памяти никогда не бывает бездействия. Мы загружаем только состояние, которое фактически потребляется/управляется

Таким образом, подумайте о PHP через HTTP как о событии, управляемом на уровне макросов, в то же время сохраняя уверенность, что любое задание действительно детерминировано и фактически не управляется событиями. Тем не менее, мы реализуем шаблон фронт-контроллера и MVC, чтобы мы могли предоставить разработчикам приложений привычную структуру даже ведомых крючков. Когда вы работаете с достойной структурой, вы просто говорите: "Я хочу знать, когда пользователь регистрируется, и пользователь должен быть доступен мне для изменения в это время". Это развитие событий. Вы не должны беспокоиться о том, что инфраструктура загрузила среду для (почти) единственной цели вызова вашего крюка (по сравнению с более традиционным понятием, что среда уже была там, и вы просто были уведомлены о событии). Это то, что означает развитие PHP в управляемом событиями пути. Контроллеры определяют (на основе запроса), какое событие происходит, и использует какой-либо механизм, который он предназначен для использования (например, шаблон наблюдателя, архитектура захвата и т.д.), Чтобы позволить вашему коду обрабатывать событие, отвечать на событие или любую номенклатуру наиболее подходит для вашей конкретной семантики рамки.