Java 为什么人们在他们的代码中使用消息/事件总线?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3987391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why people use message/event buses in their code?
提问by iirekm
I think that you have heard of message/event buses, it's the single place when all events in the system flow. Similar architectures are found in computer's motherboards and LAN networks. It's a good approach for motherboards and networks as it reduces the number of wires, but is it good for software development? We don't have such restrictions as electronics does.
我想你听说过消息/事件总线,它是系统中所有事件流动的唯一地方。在计算机的主板和 LAN 网络中可以找到类似的架构。这是主板和网络的好方法,因为它减少了电线的数量,但它对软件开发有好处吗?我们没有电子产品那样的限制。
The simplest implementation of message bus/event bus can be like:
消息总线/事件总线的最简单实现可以是这样的:
class EventBus {
void addListener(EventBusListener l}{...}
void fireEvent(Event e) {...}
}
Posting events is done with bus.fireEvent(event), receiving messages is enabled by bus.addListener(listener). Such architectures are sometimes used for software development, for example MVP4G implements similar message bus for GWT.
发布事件由 bus.fireEvent(event) 完成,接收消息由 bus.addListener(listener) 启用。这种架构有时用于软件开发,例如 MVP4G 为 GWT 实现了类似的消息总线。
Active projects:
活跃项目:
- Google Guava EventBus
- MBassadorby Benjamin Diedrichsen
- Mycila PubSubby Mathieu Carbou
- mvp4g Event Bus
- Simple Java Event Bus
Dormant/Dead projects:
休眠/死亡项目:
- Sun/Oracle JavaBeans InfoBus
- https://eventbus.dev.java.net/[Broken link]
- Sun/Oracle JavaBeans 信息总线
- https://eventbus.dev.java.net/[断开的链接]
It's just the popular Observer (Listener) pattern made 'globally' - each object in the system can listen to each message, and I think it's bad, it breaks the Encapsulation principle (each object knows about everything) and Single Responsibility principle (eg when some object needs to a new type of message, event bus often needs to be changed for example to add a new Listener class or a new method in the Listener class).
这只是流行的“全局”观察者(监听器)模式——系统中的每个对象都可以监听每条消息,我认为这很糟糕,它打破了封装原则(每个对象都知道一切)和单一职责原则(例如,当某些对象需要一种新的消息类型,通常需要更改事件总线,例如添加新的 Listener 类或 Listener 类中的新方法)。
For these reasons I think, that for most software, Observer pattern is better than event bus. What do you think about event bus, does it make any good sense for typical applications?
由于这些原因,我认为对于大多数软件来说,观察者模式比事件总线更好。您如何看待事件总线,它对典型应用程序是否有意义?
EDIT: I'm not talking about 'big' enterprise solutions like ESB - they can be useful (what's more ESB offers much, much more than just an event bus). I'm asking about usefulness of using message bus in 'regular' Java code for object-to-object connection - some people do it, check the links above. Event bus is probably best solution for telephone-to-telephone communication or computer-to-computer communication because each telefone (or computer) in a network can typically talk to each other, and bus reduces the number of wires. But objects rarely talk to each other - how many collaborators one object can have - 3, 5?
编辑:我不是在谈论像 ESB 这样的“大”企业解决方案——它们可能很有用(而且 ESB 提供的不仅仅是一个事件总线)。我在询问在“常规”Java 代码中使用消息总线进行对象到对象连接的有用性 - 有些人这样做,请查看上面的链接。事件总线可能是电话到电话通信或计算机到计算机通信的最佳解决方案,因为网络中的每个电话(或计算机)通常可以相互通信,而总线减少了电线的数量。但是对象很少互相交谈——一个对象可以有多少个合作者——3、5?
采纳答案by duffymo
Some people like it because it is the embodiment of the Facade patternor Mediator pattern. It centralizes cross-cutting activities like logging, alerting, monitoring, security, etc.
有些人喜欢它,因为它是Facade 模式或Mediator 模式的体现。它集中了跨领域活动,如日志记录、警报、监控、安全等。
Some people don't like it because it is often a Singleton point of failure. Everyone has to know about it.
有些人不喜欢它,因为它通常是单例故障点。每个人都必须了解它。
回答by questzen
A good analogy is that of a telephone exchange, where every handset can dial to every other handset. A compromised handset can tune into other conversations. Program control flows like wires(cyclomatic complexity anyone!) This is similar to the requirement of having a connection/physical medium between two end points. This is So for N handsets instead of having NC2 (Combinatorial logic) flows for every new handset we tend to get N flows.
一个很好的类比是电话交换机,其中每个手机都可以拨打其他手机。受损的手机可以调入其他对话。程序控制流程就像电线一样(圈复杂度任何人!)这类似于在两个端点之间具有连接/物理介质的要求。对于 N 部手机,我们倾向于获得 N 部手机,而不是每部新手机都有 NC2(组合逻辑)流。
A reduction in complexity implies easy to understand code. Lets start with the prominent points you have highlighted: 1. Global knowledge 2. Intrusive modifications.
复杂性的降低意味着易于理解的代码。让我们从您突出显示的重点开始: 1. 全局知识 2. 侵入性修改。
Global Knowledge: Consider message event to be an envelop. From event handler/sender perspective there is no data being exposed, it is seeing an envelop (unless an derived class tries to do some inspection using 'instanceof' checks). In a good OOP design, this would never occur.
全局知识:将消息事件视为一个信封。从事件处理程序/发送方的角度来看,没有公开数据,它看到的是一个信封(除非派生类尝试使用“instanceof”检查进行一些检查)。在一个好的 OOP 设计中,这永远不会发生。
Intrusive modifications: Instead of having a event specific listener approach, one can use a global event handling approach. As such we have a global event type (on which data is piggy backed and down-casted). This is much like the PropertyBeanSupport model in Java. With a single event type we are required to have a single sender and listener types. This implies you need not modify the bus/listeners every time you see something new. The ugly down-casting can be soothened using Adapter pattern (Please don't start that another level of redirection quote!). Programmers can write assembly in any language. So need for commonsense and smartness can not be substituted. All I intend to state is it can be a effective tool.
侵入性修改:可以使用全局事件处理方法,而不是使用特定于事件的侦听器方法。因此,我们有一个全局事件类型(数据被捎带和向下转换)。这很像 Java 中的 PropertyBeanSupport 模型。对于单一的事件类型,我们需要有单一的发送者和监听者类型。这意味着每次看到新内容时都不需要修改总线/侦听器。使用适配器模式可以缓解丑陋的向下转换(请不要开始另一个级别的重定向引用!)。程序员可以用任何语言编写汇编。所以常识和智慧的需要是不可替代的。我只想说明它可以成为一种有效的工具。
The actual event receivers can use the listeners (composition/proxy) easily. In such a Java code base, listeners would look like stand alone inner class declarations (with unused warning being flagged in certain IDEs). This is akeen to two players on the beach playing a ball game, the players don't react until they see the ball.
实际的事件接收器可以轻松地使用侦听器(组合/代理)。在这样的 Java 代码库中,侦听器看起来像是独立的内部类声明(在某些 IDE 中标记了未使用的警告)。这很像沙滩上的两个球员在玩球类游戏,直到他们看到球才会做出反应。
'@duffymo' points out another interesting aspect: 'Single point of failure'. This would/can in theory effect any object residing in memory(RAM) and not specific to MessageHandlers.
“@duffymo”指出了另一个有趣的方面:“单点故障”。这在理论上会/可以影响驻留在内存(RAM)中的任何对象,而不是特定于 MessageHandlers。
回答by Qwerky
I'm having trouble understanding what you're reallyasking in your question. You give an example of a simple event bus which is actually just Observable
with a different name, then you say;
我无法理解您在问题中真正问的是什么。你举了一个简单的事件总线的例子,它实际上只是Observable
一个不同的名字,然后你说;
For these reasons I think, that for most software, Observer pattern is better than event bus. What do you think about event bus, does it make any good sense for typical applications?
由于这些原因,我认为对于大多数软件来说,观察者模式比事件总线更好。您如何看待事件总线,它对典型应用程序是否有意义?
..but given your example, they are the same. This makes me wonder if you have ever used something like a Enterprise Service Bus. At a base level an ESB logically does the same thing as the observer pattern, but commercial products add much, much more. Its like an event bus on steroids. They are complicated software products and offer;
..但以您的示例为例,它们是相同的。这让我想知道您是否曾经使用过诸如企业服务总线之类的东西。在基本级别,ESB 在逻辑上与观察者模式做同样的事情,但商业产品添加了更多、更多。它就像类固醇的事件总线。它们是复杂的软件产品和报价;
Message pickup
Generate events by listening to various endpoints. The endpoint can be a listener (such as a HTTP server), a messaging system (such as JMS), a database or pretty much anything else you want.
消息拾取
通过侦听各种端点生成事件。端点可以是侦听器(例如 HTTP 服务器)、消息传递系统(例如 JMS)、数据库或几乎任何其他您想要的东西。
Message routing
Take your event and send it to one/many endpoint. Routing can be pretty smart, the bus might route the message depending on the message type, the message contents or any other criteria. Routing can be intelligent and dynamic.
消息路由
获取您的事件并将其发送到一个/多个端点。路由可能非常智能,总线可能会根据消息类型、消息内容或任何其他标准来路由消息。路由可以是智能的和动态的。
Message Transformation
Transforms your message into another format, this can be as simnple as from XML to JSON or from a row on a database table to a HTTP request. Transformation can occur within the data itself, for example swapping date formats.
消息转换
将您的消息转换为另一种格式,这可以像从 XML 到 JSON 或从数据库表上的行到 HTTP 请求一样简单。转换可以发生在数据本身内,例如交换日期格式。
Data Enrichment
Adds or modifies data in your message by calling services along the way. For example if a message has a postcode in it the bus might use a postcode lookup service to add in address data.
数据丰富
通过一路调用服务来添加或修改消息中的数据。例如,如果消息中包含邮政编码,则总线可能会使用邮政编码查找服务来添加地址数据。
..and lots, lots more. When you start looking into the details you can really start to see whypeople use these things.
..还有很多很多。当你开始寻找到的细节,你可以真正开始看到为什么人们使用这些东西。
回答by Bal
I use it heavily in JavaScript. There can be so many various widgets that all need to do some sort of action whenever something else happens -- there is no real hierarchy of ownership of objects. Instead of passing references of every object to every object, or just making every object global, when something significant happens inside a particular widget, I can just publish "/thisWidget/somethingHappened" -- instead of filling that widget with all kinds of code specific to the API of other widgets. The I have a single class that contains all the "wiring", or "plubming" as they like to call it in the Java Spring framework. This class contains references to all of my widgets, and has all of the code for what happens after each various event fires.
我在 JavaScript 中大量使用它。可能有很多不同的小部件,它们都需要在发生其他事情时执行某种操作——没有真正的对象所有权层次结构。而不是将每个对象的引用传递给每个对象,或者只是将每个对象设为全局,当在特定小部件内部发生重大事件时,我可以发布“/thisWidget/somethingHappened”——而不是用各种特定的代码填充该小部件到其他小部件的 API。我有一个包含所有“布线”或“管道”的类,因为他们喜欢在 Java Spring 框架中调用它。这个类包含对我所有小部件的引用,并包含每个不同事件触发后发生的所有代码。
It is centralized, easy to access and maintain, and if one thing changes or I want a new process to occur on a specific event, I don't have to search through every single class/object/widget to try to find out where something is being handled. I can just go to my "operator" class -- the one that handles all the "wiring" when a particular event happens, and see every repercussion of that event. In this system, every individual widget is completely API agnostic of the other widgets. It simply publishes what has happened to it or what it is doing.
它是集中式的,易于访问和维护,如果某件事发生变化或者我希望在特定事件上发生一个新流程,我不必搜索每个类/对象/小部件来尝试找出某些东西正在处理。我可以去我的“操作员”类——当特定事件发生时处理所有“接线”的类,并查看该事件的每一个影响。在这个系统中,每个单独的小部件都完全与其他小部件的 API 无关。它只是发布它发生了什么或它正在做什么。
回答by Sudarshan
I am considering using a In memory Event Bus for my regular java code and my rationale is as follows
我正在考虑为我的常规 java 代码使用内存事件总线,我的基本原理如下
Each object in the system can listen to each message, and I think it's bad, it breaks the Encapsulation principle (each object knows about everything)
系统中的每个对象都可以监听每条消息,我认为这很糟糕,它打破了封装原则(每个对象都知道一切)
I am not sure if this is really true, I class needs to register with the event bus to start with, similar to observer pattern, Once a class has registered with the Event Bus, only the methods which have the appropriate signature and annotation are notified.
我不确定这是否真的如此,我的类需要在事件总线上注册才能开始,类似于观察者模式,一旦一个类在事件总线上注册,只有具有适当签名和注释的方法会被通知.
and Single Responsibility principle (eg when some object needs to a new type of message, event bus often needs to be changed for example to add a new Listener class or a new method in the Listener class).
和单一职责原则(例如,当某个对象需要一种新类型的消息时,通常需要更改事件总线,例如添加一个新的 Listener 类或 Listener 类中的新方法)。
I totally disagree with
我完全不同意
event bus often needs to be changed
事件总线经常需要改变
The event bus is never changed
事件总线永远不会改变
I agree with
我同意
add a new Listener class or a new method in the Listener class
How does this break SRP ?, I can have a BookEventListener which subscribes to all events pertaining to my Book Entity, and yes I can add methods to this class but still this class is cohesive ...
这如何破坏 SRP ?,我可以有一个 BookEventListener 订阅与我的 Book 实体有关的所有事件,是的,我可以向此类添加方法,但此类仍然具有凝聚力......
Why I plan to use it ? It helps me model the "when" of my domain ....
为什么我打算使用它?它帮助我为我的域的“何时”建模......
Usually we hear some thing like send a mail "when"book is purchased
通常我们会听到诸如“何时”购买书籍之类的消息
we go write down
我们去写下来
book.purchase();
sendEmail()
Then we are told add a audit log when a book is purchased , we go to the above snippet
然后我们被告知在购买一本书时添加审核日志,我们转到上面的代码段
book.purchase();
sendEmail();
**auditBook();**
Right there OCPviolated
就在那里违反了OCP
I Prefer
我更喜欢
book.purchase();
EventBus.raiseEvent(bookPurchasedEvent);
Then keep adding handlers as needed Open for Extension Closed for Modification
然后根据需要继续添加处理程序 Open for Extension 关闭修改
Thanks
谢谢
回答by brunocrt
Because it can be an important step in the way to decouple the application modules to a service based architecture.
因为它可以是将应用程序模块解耦到基于服务的架构的重要一步。
So in your case if you have not the intention to decouple the modules of your application into isolated services then the native implementation of the Observer patternwill make it a simpler solution.
因此,在您的情况下,如果您不打算将应用程序的模块解耦为隔离的服务,那么观察者模式的本机实现将使其成为一个更简单的解决方案。
But If you want to build lets say a micro-services architecturethe event-bus will allow to get the benefits of this architecture style so you could for instance update and deploy just some part of your application without affect others, because they are just connected through the event-bus.
但是如果你想构建一个微服务架构,事件总线将允许获得这种架构风格的好处,因此你可以例如更新和部署应用程序的一部分而不影响其他部分,因为它们只是连接通过事件总线。
So the main question here is the desired level of application components decoupling.
所以这里的主要问题是应用程序组件解耦所需的级别。
Some references about it:
关于它的一些参考:
回答by Captain Kenpachi
As a practical example, our app sync's with a web service every x number of minutes, and if any new data is received, we need to update the GUI. Now, because the SyncAdapter runs on a background thread, you can't simply point to a textview and modify its properties, you have to bubble up an event. And the only way to make sure you catch that event is if you have a shared (static,singleton) object passing that event around for subscribers to handle.
作为一个实际示例,我们的应用程序每 x 分钟与 Web 服务同步一次,如果收到任何新数据,我们需要更新 GUI。现在,因为 SyncAdapter 在后台线程上运行,您不能简单地指向 textview 并修改其属性,您必须冒泡一个事件。确保捕获该事件的唯一方法是,如果您有一个共享(静态,单例)对象传递该事件以供订阅者处理。