39

Currently, I am using an EventBus/PubSub architecture/pattern with Scala (and JavaFX) to implement a simple note organizing app (sort of like an Evernote client with some added mind mapping functionality) and I have to say that I really like EventBus over the observer pattern.

Here are some EventBus libraries :

https://code.google.com/p/guava-libraries/wiki/EventBusExplained

http://eventbus.org (currently seems to be down) this is the one I am using in my implementation.

http://greenrobot.github.io/EventBus/

Here is a comparison of EventBus libraries : http://codeblock.engio.net/37/

EventBus is related to the publish-subscribe pattern.

However !

Recently, I took the Reactive course by Coursera and started to wonder whether using RXJava instead of EventBus would simplify the event handling code even more in a single threaded application ?

I would like to ask about the experiences of people who programmed using both technologies (some kind of eventbus library and some form of the reactive extensions (RX)): was it easier to tackle event handling complexity using RX than with an event bus architecture given that there was no need to use multiple threads ?

I am asking this because I have heard in the Reactive Lectures on Coursera that RX leads to much cleaner code than using the observer pattern (i.e. there is no "callback hell"), however I did not find any comparison between EventBus architecture vs RXJava. So it's clear that both EventBus and RXJava are better than the observer pattern but which is better in a single threaded applications in terms of code clarity and maintainability ?

If I understand correctly the main selling point of RXJava is that it can be used to produce responsive applications if there are blocking operations (e.g. waiting for response from a server).

But I don't care about asychronicity at all, all I care about is keeping the code clean, untangled and easy to reason about in a single threaded application.

In that case, is it still better to use RXJava than EventBus ?

I think EventBus would be a simpler and cleaner solution and I don't see any reason why I should use RXJava for a single threaded application in favour of a simple EventBus architecture.

But I might be wrong!

Please correct me if I am wrong and explain why RXJava would be better than a simple EventBus in case of a single threaded application where no blocking operations are carried out.

jhegedus
  • 18,516
  • 11
  • 84
  • 147
  • 2
    You might want to get in touch with Tomas Mikula, the [ReactFX](https://github.com/TomasMikula/ReactFX) creator. – jewelsea Mar 25 '14 at 21:46
  • Interesting! Thanks for the tip. – jhegedus Mar 25 '14 at 21:55
  • 1
    scalafx (and javafx) have their own `Observable` class (actually `ObservableValue` is closer to RX' observable). I'm currently looking into creating an adapter between the two. If this were possible, you could simply use scalafx's `bind` directive, which is nice and declarative! – Luciano May 21 '14 at 14:01
  • 1
    I don't know what "Eventbus" is. That being said, event buses are service locators, which are anti-patterns. Using the pub/sub pattern with dependency injection, on the other hand, gets you a lot more explicit control over application modularity, and therefore testing as well. Since Rx is absolutely fantastic for pub/sub, I'd advice that. Again, if Eventbus is something other than an event bus, then I have no advice on the matter. – cwharris May 22 '14 at 20:55
  • @ChristopherHarris EventBus is a kind of pub/sub pattern. What do you mean pub/sub pattern with dependency injection ? Why do I need DI for pub/sub ? The real question is do I really need Rx over pub/sub in a **single threaded** application ? What does Rx give me more in a single threaded application than what a simple pub/sub library can give me ? Please see modified and extended question for references to such libraries. – jhegedus Jul 22 '14 at 23:08
  • 2
    I would say that event streams (be it RX or ReactFX) would improve code clarity because 1) event bus is (or feels like) a global object and 2) event streams come with handy stream operators to filter/transform/combine events which, when used properly, can encapsulate a lot of state, reducing mutable state and side effects in your application. Maybe we can advise more if you post some concrete problem or code. – Tomas Mikula Jul 23 '14 at 00:01
  • Thank you very much for this answer Tomas ! I will try to come up with some model scenario in which both approaches could be compared and the advantages vs disadvantages can be pointed out in a more specific manner. – jhegedus Jul 23 '14 at 12:16
  • @TomasMikula Tomas, could you perhaps describe an example in an answer that you have on your mind where RX would be better than EventBus? I mean the simplest possible concrete example where RX would beat EventBus in terms of simplicity/understandability/code clarity. I don't see why having a globally shared event channel would be any problem if the number of possible different events (event classes in a hierarchy) is not more than about 100. My problem with RX is that it needs dependency injection, i.e. the objects that send or receive events have to be injected with the channel. – jhegedus Jul 23 '14 at 15:38
  • So the basic difference between a single channel EventBus and RX is that in RX you have much more channels via which the events can travel, right? In EventBus you have only one channel and this simplifies the application because there is only one channel. Like a CB channel, everybody can listen to the messages but don't have to react if the message is not interesting. I specifically have in mind GUI events which change a domain model which again updates the view. All communication can go on the same channel, why would RX help ? – jhegedus Jul 23 '14 at 15:45
  • 2
    When you have a component A that consumes events produced by component B, their coupling via an event bus is indirect and therefore less clear. `a.events().subscribe(b::handle)` is much more explicit than `eventBus.register(b)` somewhere and `eventBus.post(evt)` somewhere else. Furthermore, the producer's API does not state what types of events it publishes to the event bus. On the other hand, if you have a component that has a method returning `EventStream` and another one returning `EventStream`, it is clear that this component produces events of type `E` and events of type `F`. – Tomas Mikula Jul 24 '14 at 00:41
  • 1
    You don't necessarily need dependency injection, at least not with ReactFX. A component that produces events will _create_ an event stream instead of getting it as a dependency. A component that reacts to events may just expose event handling methods, or an event _sink_ to which events can be pushed. The wiring between the producer and the consumer is set up in a component encapsulating the two. – Tomas Mikula Jul 24 '14 at 00:50
  • So basically you say that RX is better because the wiring logic is located in one place instead of scattered all over the place (which is what happens when using EventBus). – jhegedus Jul 24 '14 at 19:09
  • Also Tomas, if you would kindly add your above comments as an answer then I could accept that and the question then would be properly answered. – jhegedus Jul 24 '14 at 21:34
  • Yes, but that's not the main strength of reactive event streams, but rather just a disadvantage of event buses. The main strength (in a synchronous single threaded application) is reducing mutable state and side effects. ReactFX (but not rxJava) also has means to eliminate _glitches_ (temporary inconsistencies in observed state) and redundant computation. Whether you can take advantage of these features depends on your use case. Direct rewrite to RX does not automatically yield these benefits. – Tomas Mikula Jul 24 '14 at 23:28
  • Could you please give a simple example how RX reduces mutable state vs EventBus ? It would be very illuminating for me to see how this can happen. – jhegedus Jul 24 '14 at 23:37

4 Answers4

30

The following is what I see as benefits of using reactive event streams in a single-threaded synchronous application.

1. More declarative, less side-effects and less mutable state.

Event streams are capable of encapsulating logic and state, potentially leaving your code without side-effects and mutable variables.

Consider an application that counts button clicks and displays the number of clicks as a label.

Plain Java solution:

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!!!
});

ReactFX solution:

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());

No mutable variable is used and the side-effectful assignment to label.textProperty() is hidden behind an abstraction.

In his master thesis, Eugen Kiss has proposed integration of ReactFX with Scala. Using his integration, the solution could look like this:

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)

It is equivalent to the previous, with the additional benefit of eliminating inversion of control.

2. Means to eliminate glitches and redundant computations. (ReactFX only)

Glitches are temporary inconsistencies in observable state. ReactFX has means to suspend event propagation until all updates to an object have been processed, avoiding both glitches and redundant updates. In particular, have a look at suspendable event streams, Indicator, InhiBeans and my blog post about InhiBeans. These techniques rely on the fact that event propagation is synchronous, therefore do not translate to rxJava.

3. Clear connection between event producer and event consumer.

Event bus is a global object that anyone can publish to and subscribe to. The coupling between event producer and event consumer is indirect and therefore less clear. With reactive event streams, the coupling between producer and consumer is much more explicit. Compare:

Event bus:

class A {
    public void f() {
        eventBus.post(evt);
    }
}

// during initialization
eventBus.register(consumer);
A a = new A();

The relationship between a and consumer is not clear from looking at just the initialization code.

Event streams:

class A {
    public EventStream<MyEvent> events() { /* ... */ }
}

// during initialization
A a = new A();
a.events().subscribe(consumer);

The relationship between a and consumer is very explicit.

4. Events published by an object are manifested in its API.

Using the example from the previous section, in the event bus sample, A's API does not tell you what events are published by instances of A. On the other hand, in the event streams sample, A's API states that instances of A publish events of type MyEvent.

Zombies
  • 22,793
  • 38
  • 132
  • 216
Tomas Mikula
  • 6,357
  • 22
  • 37
  • 9
    On your point #3, the fact that the "wiring" is loose is a strength of event bus rather than a weakness in my opinion. From the perspective of the consumer, it really does not matter who publishes an event. And vice versa, from the publisher's perspective, it does not care about who receives it - whoever wants to listen and react, can. That is the beauty of the pub/sub architecture. At least that is my understanding of it. – Jiho Han Sep 22 '15 at 15:04
  • 2
    @JihoHan What you say is still true about my example in point 3: `A` does not care who, if anyone, consumes the events it produces, and `consumer` does not care who produced the event. The added benefit is that the wiring code knows for sure that `A` is capable of producing events of type `MyEvent`, and that `consumer` is capable of consuming them. This gives you more type safety. Too much freedom (as with global event bus) reduces the ability to reason about the code. – Tomas Mikula Sep 23 '15 at 15:23
  • 1
    @TomasMikula Totally agree with you on this one - I sometimes feel like a poorly executed bus is like littering your code with global variables... A well executed Bus isn't a problem, but I find with Observables - it's much easier to execute it well. – SJoshi Jan 27 '16 at 19:30
5

I think you have to use the rxjava because it provides much more flexibility. If you need a bus you can use an enum like this:

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 (...);

.

Roger Garzon Nieto
  • 6,406
  • 2
  • 26
  • 24
  • Why an enum and not just a class? – cyroxis Apr 20 '16 at 13:44
  • 1
    @cyroxis You use the "enum-pattern" because it is the usual way to implement Singletons in Java. But in this particular case the enum is final and prohibit instantiations, so you don't need to write a private constructor. – Christian Ullenboom Feb 21 '17 at 18:53
  • 1
    @ChristianUllenboom This is not a singleton, just a static field that could go in a class or interface. Also I would not say that Enum's are the usual way, it does not support lazy instantiation which can be important when load times are a concern (e.g. Android). – cyroxis Mar 02 '17 at 18:12
1

As per my comment above, JavaFx has a class ObservableValue which sort of corresponds to RX Observable (probably ConnectableObservable to be more precise, as it allows more than one subscription). I use the following implicit class to convert from RX to JFX, like this:

import scala.collection.mutable.Map
import javafx.beans.InvalidationListener
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
import rx.lang.scala.Observable
import rx.lang.scala.Subscription

/**
 * Wrapper to allow interoperability bewteen RX observables and JavaFX
 * observables. 
 */
object JfxRxImplicitConversion {
  implicit class JfxRxObservable[T](theObs : Observable[T]) extends ObservableValue[T] { jfxRxObs =>
    val invalListeners : Map[InvalidationListener,Subscription] = Map.empty
    val changeListeners : Map[ChangeListener[_ >: T],Subscription] = Map.empty
    var last : T = _
    theObs.subscribe{last = _}

    override def getValue() : T = last 

    override def addListener(arg0 : InvalidationListener) : Unit = {
      invalListeners += arg0 -> theObs.subscribe { next : T => arg0.invalidated(jfxRxObs) }
    }

    override def removeListener(arg0 : InvalidationListener) : Unit = {
      invalListeners(arg0).unsubscribe
      invalListeners - arg0
    }

    override def addListener(arg0 : ChangeListener[_ >: T]) : Unit = {
      changeListeners += arg0 -> theObs.subscribe { next : T => arg0.changed(jfxRxObs,last,next) }
    }

    override def removeListener(arg0 : ChangeListener[_ >: T]) : Unit = {
      changeListeners(arg0).unsubscribe
      changeListeners - arg0
    }
  }
}

Then allows you to use property bindings like so (this is ScalaFX, but corresponds to Property.bind in JavaFX):

new Label {
    text <== rxObs
}

Where rxObs could be for example:

val rxObs : rx.Observable[String] = Observable.
  interval(1 second).
  map{_.toString}.
  observeOn{rx.lang.scala.schedulers.ExecutorScheduler(JavaFXExecutorService)} 

which is simply a counter that increments every second. Just remember to import the implicit class. I can't imagine it getting any cleaner than that!

The above is a bit convoluted, due to the need to use a scheduler that plays nicely with JavaFx. See this question for a link to a Gist of how JavaFXExecutorService is implemented. There is an enhancement request for scala RX to make this into an implicit argument, so in future you may not need the .observeOn call.

Community
  • 1
  • 1
Luciano
  • 2,206
  • 1
  • 18
  • 31
1

I learned one or two things since I asked this question 2 years ago, here is my current understanding (as explained in Stephen's FRP book):

The both try to help to describe a state machine, i.e. describe how the state of the program is changing in response to events.

The key difference between EventBus and FRP is compositionality:

  • What is compositional ?

  • FRP is a compositional way of describing a state machine, and event-bus is not. Why ?

    • It describes a state machine.
    • It is compositional because the description is done by using pure functions and immutable values.
  • EventBus is not a compositional way of describing a state machine. Why not ?

    • You cannot take any two event busses and compose them in a way that gives a new, composed event bus describing the composed state machine. Why not ?
      • Event busses are not first class citizens (as opposed to FRP's Event/Stream) .
        • What happens if you try to make event busses first class citizens ?
          • Then you get something resembling FRP/RX.
      • State influenced by the event busses are not
        • first class citizens (i.e. referentially transparent, pure values in contrast to FRP's Behaviour/Cell)
        • tied to the event busses in a declarative/functional way, instead state is modified imperatively triggered by event handling

In summary, EventBus is not compositional, because the meaning and behaviour of a composed EventBus (i.e. the time evolution of the state that is influenced by said composed EventBus) depends on time (i.e the state of those parts of the software which are not included explicitly in the declaration of the composed EventBus). In other words, if I would try to declare a composed EventBus then it would not be possible to determine (just by looking at the declaration of the composed EventBus) what rules govern the state evolution of those states that are influenced by the composed EventBus, this is in contrast to FRP, where this can be done.)

Community
  • 1
  • 1
jhegedus
  • 18,516
  • 11
  • 84
  • 147