EventBus/PubSub vs (reactive extensions) RX with respect to code clarity in a single threaded application

前端 未结 4 1526
有刺的猬
有刺的猬 2020-12-22 22:42

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 ad

4条回答
  •  星月不相逢
    2020-12-22 23:04

    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.

提交回复
热议问题