为什么人们在代码中使用消息/事件总线?

时间:2010-10-21 12:25:41

标签: java events design-patterns event-handling

我认为您已经听说过消息/事件总线,它是系统中所有事件流动的单一位置。在计算机的主板和LAN网络中可以找到类似的架构。这对于主板和网络来说是一个很好的方法,因为它减少了电线的数量,但它对软件开发有好处吗?我们没有像电子产品那样的限制。

消息总线/事件总线的最简单实现可以是:

class EventBus {
    void addListener(EventBusListener l}{...}
    void fireEvent(Event e) {...}
}

使用bus.fireEvent(event)完成发布事件,bus.addListener(侦听器)启用接收消息。这种架构有时用于软件开发,例如MVP4G为GWT实现类似的消息总线。

活跃项目:

休眠/死亡项目:

它只是流行的Observer(Listener)模式“全局” - 系统中的每个对象都可以监听每条消息,我认为这很糟糕,它打破了封装原则(每个对象都知道所有内容)和单一责任原则(例如,当某个对象需要新类型的消息时,通常需要更改事件总线,例如在Listener类中添加新的Listener类或新方法。)

由于这些原因,我认为,对于大多数软件来说,Observer模式比事件总线更好。您如何看待事件总线,它对典型应用程序有什么好处?

编辑:我不是在谈论像ESB这样的“大型”企业解决方案 - 它们可能很有用(ESB提供的内容远远超过事件总线)。我在询问有关在“常规”Java代码中使用消息总线进行对象到对象连接的有用性 - 有些人这样做,请查看上面的链接。事件总线可能是电话到电话通信或计算机到计算机通信的最佳解决方案,因为网络中的每个电话(或计算机)通常可以相互通信,并且总线减少了电线的数量。但是对象很少相互交谈 - 一个对象可以有多少合作者 - 3,5?

7 个答案:

答案 0 :(得分:66)

我正在考虑在我的常规java代码中使用内存事件总线,我的理由如下

  

系统中的每个对象都可以收听每条消息,我认为是这样   坏了,它打破了封装原则(每个对象都知道   一切)

我不确定这是不是真的,我的类需要注册事件总线开始,类似于观察者模式,一旦类已经注册到事件总线,只有具有适当签名的方法和注释会被通知。

  

和单一责任原则(例如,当某个对象需要a   例如,新类型的消息,事件总线通常需要更改   在Listener类中添加新的Listener类或新方法。

我完全不同意

  

事件总线经常需要更改

事件总线永远不会改变

我同意

add a new Listener class or a new method in the Listener class

这是如何破坏SRP的?我可以拥有一个BookEventListener来订阅与我的Book Entity相关的所有事件,是的我可以为这个类添加方法但是这个类仍然具有凝聚力......

为什么我打算使用它?它帮助我模拟我的域名“何时”......

通常我们会听到一些事情,例如发送邮件“购买图书时

我们写下来

book.purchase();
sendEmail()

然后我们被告知在购买图书时添加审核日志,我们转到上面的代码片段

book.purchase();
sendEmail();
**auditBook();**

正好OCP违反了

我更喜欢

book.purchase();
EventBus.raiseEvent(bookPurchasedEvent);

然后根据需要继续添加处理程序打开扩展已关闭以进行修改

由于

答案 1 :(得分:26)

有些人喜欢它,因为它是Facade patternMediator pattern的体现。它集中了交叉活动,如日志记录,警报,监控,安全等。

有些人不喜欢它,因为它往往是单身人士的失败点。每个人都必须了解它。

答案 2 :(得分:16)

我在JavaScript中大量使用它。可能有许多不同的小部件都需要在其他事情发生时进行某种操作 - 对象的所有权没有真正的层次结构。不是将每个对象的引用传递给每个对象,或者只是使每个对象都是全局的,当特定小部件内发生重大事件时,我可以发布“/ thisWidget / somethingHappened” - 而不是用特定的各种代码填充该小部件到其他小部件的API。我有一个包含所有“接线”或“plubming”的类,因为他们喜欢在Java Spring框架中调用它。这个类包含对我所有小部件的引用,并且包含了每个各种事件触发后发生的所有代码。

它是集中的,易于访问和维护,如果有一件事发生变化或者我想在特定事件上发生新进程,我不必搜索每个单独的类/对象/小部件来尝试查找处理某事的地方。我可以去我的“操作员”类 - 在特定事件发生时处理所有“接线”的类,并看到该事件的每一个反响。在此系统中,每个窗口小部件都完全与其他窗口小部件无关。它只是简单地发布它发生的事情或它正在做什么。

答案 3 :(得分:14)

我无法理解您在问题中真正提出的问题。您举一个简单的事件总线示例,实际上只是Observable具有不同的名称,然后您说;

  

出于这些原因我认为,那是为了   大多数软件,观察者模式是   比事件总线更好。你是什​​么   想想事件总线,它做了   对典型的任何好感   应用

..但是举个例子,他们是一样的。这让我想知道你是否曾经使用过像Enterprise Service Bus这样的东西。在基础级别,ESB在逻辑上与观察者模式做同样的事情,但商业产品增加了很多,甚至更多。它就像一个类固醇的事件总线。它们是复杂的软件产品并提供;

信息提取
通过侦听各种端点生成事件。端点可以是侦听器(如HTTP服务器),消息传递系统(如JMS),数据库或其他任何您想要的东西。

邮件路由
参加您的活动并将其发送到一个/多个端点。路由可以非常智能,总线可以根据消息类型,消息内容或任何其他标准来路由消息。路由可以是智能和动态的。

消息转换
将您的消息转换为另一种格式,这可以像从XML到JSON或从数据库表上的行到HTTP请求一样简单。转换可以在数据本身内进行,例如交换日期格式。

数据丰富
通过在整个过程中调用服务来添加或修改消息中的数据。例如,如果消息中包含邮政编码,则总线可能会使用邮政编码查找服务来添加地址数据。

..还有很多,还有很多。当您开始查看详细信息时,您可以真正开始看到为什么人们使用这些内容。

答案 4 :(得分:4)

因为它可以是将应用程序模块解耦为基于服务的架构的重要一步。

因此,如果您无意将应用程序的模块分离为独立服务,那么 Observer模式的本机实现将使其成为更简单的解决方案。

但是如果你想建立一个可以说微服务架构的事件总线将允许获得这种架构风格的好处,所以你可以更新和部署你的一些部分应用程序不影响其他应用程序,因为它们只是通过事件总线连接。

所以这里的主要问题是应用程序组件分离的理想级别

关于它的一些参考:

答案 5 :(得分:2)

一个很好的类比是电话交换机,每个手机都可以拨打其他所有手机。受损手机可以调谐到其他对话。程序控制像线一样流动(任何人都有圈复杂度!)这类似于在两个端点之间建立连接/物理介质的要求。这对于N个手机来说是如此,而不是每个新手机都有NC2(组合逻辑)流,我们倾向于获得N个流。

复杂性的降低意味着易于理解的代码。让我们从突出的要点开始:1。全球知识2.侵入式修改。

全球知识:将消息事件视为信封。从事件处理程序/发送者的角度来看,没有数据被暴露,它看到一个信封(除非派生类尝试使用'instanceof'检查进行一些检查)。在良好的OOP设计中,这种情况永远不会发生。

侵入式修改:可以使用全局事件处理方法,而不是使用特定于事件的侦听器方法。因此,我们有一个全局事件类型(数据被捎带和下载)。这很像Java中的PropertyBeanSupport模型。对于单个事件类型,我们需要具有单个发件人和侦听器类型。这意味着每次看到新内容时都不需要修改总线/监听器。可以使用适配器模式来缓解丑陋的向下转换(请不要启动另一级别的重定向引用!)。程序员可以用任何语言编写程序集。因此需要常识和聪明才能取而代之。我打算说的是它可以成为一种有效的工具。

实际的事件接收器可以轻松地使用侦听器(组合/代理)。在这样的Java代码库中,侦听器看起来像独立的内部类声明(在某些IDE中标记未使用的警告)。这是对两名在沙滩上打球的球员的比赛,球员们在看到球之前不会做出反应。

'@ duffymo'指出另一个有趣的方面:​​'单点故障'。理论上,这将影响驻留在内存(RAM)中的任何对象,而不是特定于MessageHandler的对象。

答案 6 :(得分:1)

作为一个实际示例,我们的应用每x分钟同步一次Web服务,如果收到任何新数据,我们需要更新GUI。现在,因为SyncAdapter在后台线程上运行,所以您不能简单地指向文本视图并修改其属性,您必须冒泡一个事件。确保捕获该事件的唯一方法是,如果您有一个共享(静态,单例)对象传递该事件以供订阅者处理。