gpt4 book ai didi

PHP:我是否将事件驱动编程与信号感知接口(interface)(信号和插槽/观察者模式)混为一谈?

转载 作者:IT王子 更新时间:2023-10-29 00:02:56 26 4
gpt4 key购买 nike

我看到很多人说 Symfony2、Zend Framework 2 和其他人是事件驱动的。

在桌面世界中,通过事件驱动编程,我理解应用程序会在其状态发生变化时通知其观察者。

由于 PHP 应用程序是无状态的,因此无法做这样的事情。 IE。当用户使用界面时,将观察者绑定(bind)到观察变化的 View 。相反,它需要一个新的请求过程来更新 View 。所以,这不是一个事件,而是一个全新的请求 .

另一方面,还有一个类似的概念:事件驱动架构。

在这里,您可以阅读两者:

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

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

这是另一个:

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

A signal is a notification to a process that an event occurred. Signals are sometimes described as software interrupts. Signals are analogous to hardware interrupts in that they interrupt the normal flow of execution of a program; in most cases, it is not possible to predict exactly when a signal will arrive.

  • Stackoverflow [singals] tag description


而且,我之前所说的事件驱动,似乎和Qt引入的Signals and Slots Pattern(观察者模式实现)更相关

例如,有声称是事件驱动的 Prado 框架:

http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Applications (应用程序生命周期部分)

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

IIRC,这不是事件驱动的应用程序,而只是实现 observable Interface 的类使用的插件 Hook (信号和插槽)。 .我的意思是,考虑桌面应用程序使用事件的方式和无状态应用程序使用事件的方式(作为插件):第一个为整个应用程序使用事件,包括 View ,最后一个仅用于服务器端操作。

一个与面向方面的编程(带有信号和槽)相关,另一个与 无关。跨领域关注 /AOP。换句话说,它与应用程序状态更相关。

那么,这些术语之间实际上是什么关系,它们之间又有何不同?
  • 事件驱动编程
  • 事件驱动架构
  • 信号和插槽模式

  • 这些术语只是通用模式吗?因此,所有实现观察者模式的东西都可以被认为是事件驱动的吗?

    更新

    Zend 框架 2

    The article about AOP I've linked above ( http://mwop.net/blog/251-Aspects,-Filters,-and-Signals,-Oh,-My!.html ) was written by Matthew Weier O'Phinney (ZF Leader). IIRC, it doesn't have mentions about "event-driven", just signal and slots.



    Symfony 2

    The Symfony2 EventDispatcher component description doesn't have mentions about being for "event-driven" applications: http://symfony.com/doc/current/components/event_dispatcher/introduction.html It only contains references to "Events" (which, indeed, are handled by Signal and Slots).



    两个框架似乎都在信号和槽中使用拦截过滤器模式来处理请求过程中的同步事件。

    最佳答案

    Disclaimer: It's a long answer, but I think it's worth the read with all its references. And IMHO it leads to a conclusive answer.



    过去几天我一直在努力解决这个问题,如果我读对了所有内容,答案是:

    Event-driven !== Request-driven

    "[...] I find this the most interesting difference in event collaboration, to paraphrase Jon Udell: request driven software speaks when spoken to, event driven software speaks when it has something to say.

    A consequence of this is that the responsibility of managing state shifts. In request collaboration you strive to ensure that every piece of data has one home, and you look it up from that home if you want it. This home is responsible for the structure of data, how long it's stored, how to access it. In the event collaboration scenario the source of new data is welcome to forget the data the second it's passed to its Message Endpoint."

    Martin Fowler - Event Collaboration (Queries section)



    基于该断言,IIRC,现代 PHP 框架实现了观察者模式 + 拦截过滤器 + Singal 和 Slots,以便在请求周期期间触发一些事件。

    但是,尽管它采用了一些事件驱动架构的思想,但它似乎并不支持整个框架是事件驱动的(即 Symfony2 是一个事件驱动的框架)。

    We are used to dividing programs into multiple components that collaborate together. (I'm using the vague 'component' word here deliberately since in this context I mean many things: including objects within a program and multiple processes communicating across a network.) The most common way of making them collaborate is a request/response style. If a customer object wants some data from a salesman object, it invokes a method on the salesman object to ask it for that data.

    Another style of collaboration is Event Collaboration. In this style you never have one component asking another to do anything, instead each component signals an event when anything changes. Other components listen to that event and react however they wish to. The well-known observer pattern is an example of Event Collaboration.

    Martin Fowler - Focus on events (section: Using events to collaborate)



    我认为 PHP 应用程序更接近于事件驱动而不是请求驱动 仅当焦点在事件上时 .如果这些应用程序/框架仅将事件用于横切关注点 (AOP),那么它就不是事件驱动的。同样,你不会仅仅因为你有一些领域对象和单元测试就称它为测试驱动或领域驱动。

    现实世界的例子

    我选择了一些例子来说明为什么这些框架不是完全由事件驱动的。尽管有 AOP 事件,一切都是 请求驱动 :

    Note: Although, it can be adapted to be event-driven



    Zend 框架 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);
    }

    那是事件驱动的:您不知道将使用哪个路由器、调度程序和 View 渲染器的代码,您只知道将触发这些事件。您可以 Hook 几乎任何兼容的组件来监听和处理事件。组件之间没有直接通信。

    但是,有一件重要的事情要注意:这是表示层( Controller + View )。域层确实可以是事件驱动的,但您看到的几乎所有应用程序都不是这种情况。 **事件驱动和请求驱动之间存在混合:
    // albums controller
    public function indexAction()
    {
    return new ViewModel(array(
    'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
    ));
    }

    Controller 组件不是事件驱动的。它直接与服务组件通信。 相反,服务应该订阅 Controller 引发的事件 ,它是表示层的一部分。 (我将在本答案的末尾指出有关什么是事件驱动的域模型的引用资料)。

    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 组件通信,因此从请求过程开始它就不是完全事件驱动的,尽管它触发了一些事件并允许一些组件是可插入的(事实并非如此作为构造函数参数注入(inject)的 ControllerResolver 的一部分)。

    相反,要成为一个完全事件驱动的组件,它应该像在 ZF2 应用程序组件中一样:
        // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

    普拉多

    我没有足够的时间研究源代码,但起初它似乎不是内置于 SOLID 中的。大大地。无论哪种方式,类似 MVC 的框架上的 Controller 是什么,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's 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

    ... 等等

    关于PHP:我是否将事件驱动编程与信号感知接口(interface)(信号和插槽/观察者模式)混为一谈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12232874/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com