Event handling EventBus/PubSub vs(反应式扩展)RX在单线程应用程序中的代码清晰度

Event handling EventBus/PubSub vs(反应式扩展)RX在单线程应用程序中的代码清晰度,event-handling,system.reactive,reactive-programming,event-bus,rx-java,Event Handling,System.reactive,Reactive Programming,Event Bus,Rx Java,目前,我正在使用带有Scala(和JavaFX)的EventBus/architecture/pattern来实现一个简单的便笺组织应用程序(有点像Evernote客户端,带有一些额外的思维导图功能),我必须说,我真的喜欢EventBus而不是observer模式 以下是一些EventBus库: (目前似乎有所下降)这是我在实现中使用的一个 下面是EventBus库的比较: EventBus与 然而 最近,我开始思考使用而不是EventBus是否会在单线程应用程序中进一步简化事件处理代码 我

目前,我正在使用带有Scala(和JavaFX)的EventBus/architecture/pattern来实现一个简单的便笺组织应用程序(有点像Evernote客户端,带有一些额外的思维导图功能),我必须说,我真的喜欢EventBus而不是observer模式

以下是一些EventBus库:

(目前似乎有所下降)这是我在实现中使用的一个

下面是EventBus库的比较:

EventBus与

然而

最近,我开始思考使用而不是EventBus是否会在单线程应用程序中进一步简化事件处理代码

我想询问使用这两种技术(某种类型的eventbus库和某种形式的(RX))编程的人的经验:考虑到不需要使用多线程,使用RX比使用event bus架构更容易解决事件处理的复杂性吗

我问这个问题是因为我在中听说,与使用observer模式相比,使用observer模式可以产生更干净的代码(即没有“回调地狱”),但是我没有发现EventBus体系结构与observer模式之间的任何比较。因此,很明显,EventBus和RXJava都比observer模式好,但在单线程应用程序中,哪一个在代码清晰性和可维护性方面更好

如果我理解正确,它的主要卖点是,如果存在阻塞操作(例如等待服务器响应),它可以用于生成响应性应用程序

但我一点也不关心异步性,我所关心的只是在单线程应用程序中保持代码干净、不纠结、易于推理

在这种情况下,使用RXJava是否比使用EventBus更好

我认为EventBus将是一个更简单、更干净的解决方案,我看不出有任何理由支持简单的EventBus体系结构,而将RXJava用于单线程应用程序

但我可能错了


如果我错了,请纠正我,并解释为什么在单线程应用程序中,如果没有执行阻塞操作,RXJava会比简单的EventBus更好。

根据我上面的评论,JavaFx有一个类,它与RX
Observable
(更准确地说,可能是
ConnectableObservable
,因为它允许多个订阅)。我使用以下隐式类将RX转换为JFX,如下所示:

val incBtn = new Button("Increment")
val label = new Label("0")

label.text |= EventStreams.eventsOf(incBtn, ACTION)
    .accumulate(0, (n, a) => n + 1)
    .map(n => n.toString)
public enum Events {

  public static PublishSubject <Object> myEvent = PublishSubject.create ();
}

//where you want to publish something
Events.myEvent.onNext(myObject);

//where you want to receive an event
Events.myEvent.subscribe (...);
导入scala.collection.mutable.Map
导入javafx.beans.InvalizationListener
导入javafx.beans.value.ChangeListener
导入javafx.beans.value.observeValue
导入rx.lang.scala.Observable
导入rx.lang.scala.Subscription
/**
*包装器,允许RX可观察对象和JavaFX之间的互操作性
*可观察到的。
*/
对象JfxRxImplicitConversion{
隐式类JfxRxObservable[T](theObs:Observable[T])扩展了ObservableValue[T]{jfxRxObs=>
val InvalidListeners:Map[InvalidateListener,Subscription]=Map.empty
val ChangeListener:Map[ChangeListener[\u>:T],订阅]=Map.empty
var last:T=_
theObs.subscribe{last=}
覆盖def getValue():T=last
覆盖def addListener(arg0:invalizationListener):单位={
InvalistListeners+=arg0->theObs.subscribe{next:T=>arg0.invalidated(jfxRxObs)}
}
覆盖def RemovelListener(arg0:InvalizationListener):单位={
无效的侦听器(arg0)。取消订阅
无效侦听器-arg0
}
覆盖def addListener(arg0:ChangeListener[\u>:T]):单位={
changeListeners+=arg0->theObs.subscribe{next:T=>arg0.changed(jfxRxObs,last,next)}
}
覆盖def RemovelListener(arg0:ChangeListener[\u>:T]):单位={
changeListeners(arg0)。取消订阅
changeListeners-arg0
}
}
}
然后允许您像这样使用属性绑定(这是ScalaFX,但对应于JavaFX中的
property.bind
):

新标签{

text以下是我认为在单线程同步应用程序中使用反应式事件流的好处

1.更多的声明性,更少的副作用和更少的可变状态。 事件流能够封装逻辑和状态,从而使代码没有副作用和可变变量

考虑一个计算按钮点击次数并将点击次数显示为标签的应用程序

普通Java解决方案:

private int counter = 0; // mutable field!!!

Button incBtn = new Button("Increment");
Label label = new Label("0");

incBtn.addEventHandler(ACTION, a -> {
    label.setText(Integer.toString(++counter)); // side-effect!!!
});
Button incBtn = new Button("Increment");
Label label = new Label("0");

EventStreams.eventsOf(incBtn, ACTION)
        .accumulate(0, (n, a) -> n + 1)
        .map(Object::toString)
        .feedTo(label.textProperty());
class A {
    public void f() {
        eventBus.post(evt);
    }
}

// during initialization
eventBus.register(consumer);
A a = new A();
class A {
    public EventStream<MyEvent> events() { /* ... */ }
}

// during initialization
A a = new A();
a.events().subscribe(consumer);
ReactFX解决方案:

private int counter = 0; // mutable field!!!

Button incBtn = new Button("Increment");
Label label = new Label("0");

incBtn.addEventHandler(ACTION, a -> {
    label.setText(Integer.toString(++counter)); // side-effect!!!
});
Button incBtn = new Button("Increment");
Label label = new Label("0");

EventStreams.eventsOf(incBtn, ACTION)
        .accumulate(0, (n, a) -> n + 1)
        .map(Object::toString)
        .feedTo(label.textProperty());
class A {
    public void f() {
        eventBus.post(evt);
    }
}

// during initialization
eventBus.register(consumer);
A a = new A();
class A {
    public EventStream<MyEvent> events() { /* ... */ }
}

// during initialization
A a = new A();
a.events().subscribe(consumer);
没有使用可变变量,对
label.textProperty()
的副作用赋值隐藏在抽象后面

在他的硕士论文中,他提出将ReactFX与Scala集成。使用他的集成,解决方案可以如下所示:

val incBtn = new Button("Increment")
val label = new Label("0")

label.text |= EventStreams.eventsOf(incBtn, ACTION)
    .accumulate(0, (n, a) => n + 1)
    .map(n => n.toString)
public enum Events {

  public static PublishSubject <Object> myEvent = PublishSubject.create ();
}

//where you want to publish something
Events.myEvent.onNext(myObject);

//where you want to receive an event
Events.myEvent.subscribe (...);
它与前一种等效,具有消除控制反转的额外好处

2.消除故障和冗余计算的方法。(仅限ReactFX) 故障是可观察状态下的暂时不一致。ReactFX有方法暂停事件传播,直到对对象的所有更新都已处理完毕,以避免故障和冗余更新。特别是,请查看和。这些技术依赖于事件传播是同步的,因此不会转换为rxJava

3.明确事件生产者和事件消费者之间的联系。 事件总线是任何人都可以发布和订阅的全局对象。事件生产者和事件消费者之间的耦合是间接的,因此不太清楚。 对于反应性事件流,生产者和消费者之间的耦合更加明确