Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I implement the RxJava equivalent of INotifyPropertyChanged to make an Observable data model?

I'm new to Java and new to Rx. I'm in at the deep end. I'm at very early stages of writing an android app that will use an MVC (Model-View-Controller) pattern and I'm experimenting with various ways of doing that, in particular RxJava. My idea is to make my Model an Observable and my View the Observer. The model (or a wrapper class) would then emit a new copy of the model each time its data changes. In .net I'd be thinking about using INotifyPropertyChanged.

I think I need to implement a custom Observable - but how? There's plenty of documentation about how to convert collections into observables, but this doesn't fit my use-case as I don't have a collection - just a single item, the data model. Is there a way to do this? Remember I'm developing for Android so I can't use lambdas or anything fancy like that.

[Update] Thanks to André Staltz's answer, I think I am off the starting blocks. I came up with this class:

import rx.Observable;
import rx.subjects.BehaviorSubject;

public class ObservableModel<TModel>
    {
    private TModel                  modelData;
    private BehaviorSubject<TModel> modelStream;
    private boolean disposed = false;

    public ObservableModel(TModel modelData)
        {
        this.modelData = modelData;
        modelStream = BehaviorSubject.create(modelData);
        }

    public TModel getModelData()
        {
        if (disposed)
            {
            throw new UnsupportedOperationException("The object has been disposed");
            }
        return modelData;
        }

    public void setModelData(TModel modelData)
        {
        if (disposed)
            {
            throw new UnsupportedOperationException("The object has been disposed");
            }
        this.modelData = modelData;
        modelStream.onNext(this.modelData);
        }

    public Observable<TModel> getObservable()
        {
        return modelStream;
        }

    public void close()
        {
        modelStream.onCompleted();
        modelStream = null;
        modelData = null;
        disposed = true;
        }
    }
like image 952
Tim Long Avatar asked Aug 16 '14 23:08

Tim Long


1 Answers

You don't need to implement a custom Observable, because Observables are generic and are meant to be used by just specifying which type the Observable is of.

If you look at your model objects changing over time, you really have a collection of model objects over time: [modelObject1, modelObject2, modelObject3, ...]

One simple way of implementing this is to have a static Observable of type FooBarModel, that is, Observable<FooBarModel>:

public class FooBarModel {

    public static BehaviorSubject<FooBarModel> fooBarStream;

    // ...

    private int fooBarMember1;
    private int fooBarMember2;
}

Instead of Observable<FooBarModel>, we used a Subject (choose either PublishSubject or BehaviorSubject, as you wish), which is akin to an Event bus. Everytime you change fooBarMember1 or fooBarMember2 in FooBarModel, you emit the instance of FooBarModel by calling fooBarStream.onNext(this), and every subscriber of FooBarModel.fooBarStream will see the new instance of FooBarModel.

However, this is really just one flavour of solution, and there are many other ways of doing this. If your FooBarModel depends on updates from some other entity "Baz", then it might make more sense to replace the BehaviorSubject with Observable, and instead of directly feeding in new instances, you just create the Observable by using some transformation/combination functions (such as map(), flatMap(), delay(), filter(), etc) on the Observables of "Baz".


One good RxJava MVC architecture is to have all three Model, View, and Controller be Observables which are circularly dependent to each other.

The View observes updates from the Model's Observable, and exports public Observables for click events, keyboard events, inputs, etc.

The Controller observes the View's user input Observables, and processes those inputs to export them as Observables of "processed user input", in a format suitable for the Model. For instance, if the View has an Observable of search field textual content, then the Controller respectively exports an Observable of lowercase search field textual content. Another example: the Controller interprets click events from the View as database requests.

The Model observes "processed user input" from the Controller, and exports Observables of application data.

In such scenario you wouldn't necessarily need Subjects. The benefit of this approach is pure separation of concerns: no entity updates any other entity imperatively. All the Controller logic is entirely specified only in Controller, no where else. And also you have a clean picture of inputs and outputs. Entities observe other entities and export some of their data as Observables. There is no call "controller.updateThisAndThat()" in the View.

But for starters, use Subjects since they are easy to reason about, with regard to updating the observers.

like image 59
André Staltz Avatar answered Oct 29 '22 16:10

André Staltz