我见过很多人说Symfony2中,Zend框架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
的信号是一个事件发生的处理的通知。 信号有时被描述为软件中断。 信号类似于硬件中断,它们中断的程序的执行的正常流动; 在大多数情况下,这是不可能准确预测时,信号将到达。
此外,我习惯称之为事件驱动似乎更关系到使用Qt(观察者模式实现)介绍的信号和插槽模式
作为一个例子,有声称是事件驱动普拉多框架:
http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Applications (应用的生命周期部分)
http://www.pradosoft.com/docs/manual/System/TApplication.html#methodonEndRequest
IIRC,这不是一个事件驱动的应用程序,但由实现类使用,而不是仅仅插件钩(信号和槽) observable Interface
。 我的意思是,考虑到桌面应用程序中使用事件的方式和无状态的应用程序使用的事件(如plugi-NS)的方式:第一次使用事件为整个应用程序,包括意见,最后只为服务器端操作。
一个是更为相关面向方面的编程(用信号和槽),另一个不特别依赖于横切关注 / AOP。 换句话说,它更关系到应用状态。
那么,什么是事实,这些条款以及它们如何相互不同的关系?
- 事件驱动编程
- 事件驱动的架构
- 信号和槽模式
难道这些条款只是通用的模式? 因此,实现观察者模式都可以被认为是事件驱动?
UPDATE
Zend框架2
关于AOP的文章中,我上面链接( http://mwop.net/blog/251-Aspects,-Filters,-and-Signals,-Oh,-My!.html )写由马修·威尔O'Phinney( ZF负责人)。 IIRC,它没有提及有关“事件驱动”,只是信号和插槽。
Symfony的2
在Symfony2中EventDispatcher
组件说明没有提到关于是为“事件驱动”的应用: http://symfony.com/doc/current/components/event_dispatcher/introduction.html它仅包含对“事件”(其中,的确,由信号和插槽)来处理。
这两个框架似乎使用信号和插槽内的拦截过滤器模式,以便在请求进程来处理同步事件。
免责声明:这是一个很长的答案,但我认为这是值得拥有的所有引用的读取。 恕我直言这会导致一个结论性的答案。
我一直挣扎在这样的主题在过去几天中,如果我所有的正确读出,答案是:
事件驱动!==请求驱动
“[...]我觉得这个事件中合作的最有趣的差异,套用乔恩·德尔:要求驱动软件讲话时说过话,事件驱动的软件说话时,它有话要说。
这样做的结果是,管理状态转换的责任。 在请求协作您努力确保每一块数据都有一个家庭,如果你想要它,你从家里查找。 这家是负责数据的结构,它是如何长时间保存,如何访问它。 在事件协作方案的新数据源,欢迎忘记数据它传递给它的消息端点第二“。
Martin Fowler的- 事件协作(查询部分)
根据这种说法,IIRC,现代PHP框架实现了Observer模式+拦截过滤器+葛和角子机,为了请求周期中触发一些事件。
但是,尽管它采用事件驱动架构的一些想法的事实,它似乎并不支持整个框架是事件驱动(即Symfony2的是一个事件驱动framewrok)。
我们习惯于将计划成协作在一起的多个部件。 (我使用的是模糊的“组件”字在这里故意,因为在这方面,我的意思是很多事情:包括跨网络通信程序中的对象和多个进程。)作出的最常见的方式他们合作是一种请求/响应风格。 如果客户对象要由业务员对象的一些数据,它调用推销员对象上的方法来要求它的数据。
合作的另一种风格是事件的协作。 在这种风格,你永远不会有一个组件要求另做任何事情 ,而不是每个分量信号的事件有什么变化时。 其他组件侦听事件,并作出反应。然而他们希望。 公知的观察者模式是协作事件的一个例子。
Martin Fowler的- 关注事件(部分:使用活动进行合作)
我认为PHP应用程序更紧密合作,为事件驱动,不是请求驱动只在对焦上的事件 。 如果这些应用程序/框架只使用横切关注事件(AOP),那么它不是事件驱动。 在你不会把它叫做测试驱动或以相同的方式域驱动的,因为你有一些领域对象和单元测试。
现实世界的例子
我挑选了一些例子,说明为什么这些框架并不完全事件驱动的。 尽管AOP的事件,一切都请求驱动 :
注:虽然,它可以适合于事件驱动
Zend框架2
让我们来看看\ Zend的\的mvc \应用组件:
它实现了\ 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 \应用组件本身是事件驱动的,因为它不与其他组件直接通信,而是,它只是触发事件:
/**
* 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);
}
这是事件驱动的:你不知道看哪个的路由器,分发和查看渲染器将要使用的代码,你只知道这些事件将被触发。 你可以勾几乎任何兼容的组件来倾听和处理的事件。 有组件之间没有直接的沟通。
但是,有要注意的一件重要的事情:这是表示层(控制器+视图)。 领域层确实可以是事件驱动的,但是这不是几乎所有的应用程序,你看到那里的情况。 **有事件驱动和请求驱动的混合体:
// albums controller
public function indexAction()
{
return new ViewModel(array(
'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
));
}
该控制器组件不是事件驱动的。 它直接与服务组件进行通信。 取而代之的是服务应该订阅由控制器上涨事件 ,这是表示层的一部分。 (我会指出什么会是这个答案的最后一个事件驱动的领域模型的参考文献)。
Symfony的2
现在,让我们来看看同在Symfony2的应用/ FrontController: \的Symfony \分量\ HttpKernel \ HttpKernel
它确实有要求中的主要事件: Symfony的\分量\ 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的)。
取而代之的,是一个完全的事件驱动组件应该在ZF2应用程序组件:
// Trigger dispatch event
$result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);
普拉多
我没有足够的时间来研究源代码,但起初它似乎并不被建在一个SOLID方式。 无论哪种方式,你对MVC-一样框架控制器,普拉多称其为t第(还不确定):
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是不是无状态的,HTTP是。 为了简单地说明它,我们已经基本上建立在后,我们可以实现有状态的设计无状态技术之上的一层。 综上所述,PHP和您所选择的数据存储有他们所需要的基础上通过会议的标记化事件驱动的模式来构建应用程序设计的工具。
在一个高度概括的方式,你可以把HTTP作为是为网络已经什么BIOS适用于桌面计算。 事实上,进一步利用这个只是一点点,你可以很容易地看到网络的隐式事件驱动性质。 你说:“这不是一个事件,而是一个全新的请求”,和我一起,“一个全新的请求为事件”回来,我的意思是在这个词的设计模式的意义。 它与您的用户与应用程序交互的具体语义。
从本质上讲,通过像MVC和前端控制器模式(通过HTTP cookies和PHP的会话机制),我们简单地恢复会话状态,然后回应的情况下,相应地修改该状态。
我喜欢考虑REST的精髓:具象状态传输...但我想补充一点,我们不应该忘记的暗示,当发生UI事件状态只能转移。 因此,我们维持我们只在我们的模型中的“具象国”(即一个文件,JSON等)与HTTP,我们“说话”的合同,但这只是我们的方言。 其他系统选择画布坐标,信号DB等说话
编辑/更多的心思
所以,我一直在琢磨了一段时间,我认为这是通过HTTP在PHP的境界讨论这些模式时,它说明了一点模糊的概念:决定。 具体来说,一旦接收到一个请求,PHP执行路径是确定的,这就是为什么它是非常困难的考虑在PHP中的“事件驱动”的架构。 我的观点是,我们应该考虑比PHP更高的一个层次,与用户交互的更大的“会话”。
在桌面计算中,我们使用runloops和国家FUL上下文“等待”的事件。 不过,我会说,网络其实是在这个架构的改进(在大多数情况下),但最终同样的模式 。 取而代之的是runloop和无限持续时间的状态,我们引导我们的国家当事件发生时 ,然后处理该事件。 而不是只是保持这种状态在内存中,并等待下一个事件,我们存档的状态,并关闭资源。 它可以被认为是在一种意义上效率较低(我们需要在每一个“事件”来加载状态),但它也可以被称为在更高效从未有在内存中无故闲置状态 。 我们只加载这实际上是消耗/操作状态
因此,在这种方式,通过HTTP认为PHP的为事件在宏观层面驱动,而休息放心,任何给定的执行确实是确定性的,而不是真正的事件在所有驱动。 不过,在实现一个前端控制器和MVC模式,使我们可以提供应用程序开发人员甚至带动钩熟悉的结构 。 当你一个体面的框架内工作,你简单地说:“我想,当一个用户注册要知道,用户应该提供给我那个时候修改”。 这是事件驱动的开发。 它不应该关心你的框架自举的环境(几乎)调用你的钩子的唯一目的(相对于比较传统的观念,环境已经在那里了,你简单地通知的事件)。 这是什么意思开发PHP在事件驱动的方式。 所述控制器确定(基于所述请求),该事件正在发生,并取其机构它被设计用来使用(即观察者模式,钩结构,等),以允许您的代码来处理的情况下,响应该事件,或任何命名法是最适合您的特定框架的语义。
文章来源: PHP: Am I mixing up event-driven programming with signals-aware interfaces (Signal and Slots / Observer Pattern)?