JavaFX - Observable Collections in the data model classes

Here follows a simple piece of JavaFX code, to illustrate my question.

List list1 = new ArrayList();
list1.add("foo");
...

someListView = new ListView<>();
ObservableList someObservableList = FXCollections.observableList(list1);
someListView.setItems(someObservableList);
...

someObservableList.add("bar");

If I understood correctly, after calling the method, not only will the content of the list be shown in the Gui component, but also if items are added to the instance afterwards, the will be refreshed automatically and will show the newly added items automatically, without the need to call any additional or methods.setItemsListViewObservableListListViewaddrefresh

So far, so good. But what if I add something to the original list (i.e. ). These changes are not propagates automatically. It makes perfect sense, but it is inconvenient sometimes.list1

Of course, in a classic Java application the Model of the application does not consist of instance. So, whenever you add something to the model, you will always have to update the instances that were derived from the original list. Apparently this is inevitable, right ?ObservableCollectionObservableLists

This got me wondering, is it a clever idea to modify type occurrences (e.g. , , , , ...) in the Model classes and replace them by their alternatives from now on ? CollectionListCollectionSetIterableObservableCollection

Until now I always figured that these classes were only supposed to be used in the Gui layer of the applicaiton, but it seems pretty convenient to use them about everywhere.ObservableCollection


答案 1

You can hack through Granite Data Services Generator (written in groovy GSP and Java) to generate "bindable" java classes from your domain classes by generating javafx properties to hold data for basic fields including collections.

There is a great sample project for that: https://github.com/graniteds/shop-admin-javafx which is built with maven with the generation enabled. This refers to DRY principle (Don't Repeat Yourself) and sometimes it seems more useful that domain classes become bindables. You have modified your classes to have javafx properties: you have done something similar to Granite and yes, it has some advantages as it removes boilerplate code (similar to this: EJB3 seem to remove DTO, the entities are the domain classes). But your domain classes become tightly coupled with your views: it looks like a MVC pattern.

Another solution is to use the JFXtras library which has an interesting API to generate on the fly listeners on java collections to instanciate a javafx property. Here is a link where it is described: http://ugate.wordpress.com/2012/07/30/javafx-programmatic-pojo-expression-bindings-part-iii/

With JFXtras you can use the properties only when you want them, with an API that is relatively simple (for collections, it can become difficult to read IMHO) and this way you don't modify your domain classes that won't become View related. But for advanced bindings on collections: it does not seem to be the best solution. It looks like a compromise between MVC and MVP.

The last solution I see is to stick with MVP pattern, you are actually using it, which originally does not allow Model classes to be accessed through the View by introducing a Presenter layer to link the two: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter

You will have to instanciate and synchronize values manually between your javafx properties and your domain classes: sometimes it is what you want and the only way to achieve this (for instance: this way you can 'revert' to old domain value in a form quite easily, if it was bindable you would have lost the last 'correct' domain value). Note that you can put your javafx properties in singletons (using an IOC framework such as Spring) to be accessed and binded through different views: I see them as part of the Presenter.

I don't think there is a better solution: choose according to your needs and your preferences. It can even become a classic MVC vs MVP debate where there is no winner.


答案 2

Generally speaking, I would avoid any GUI-Library dependencies in my model layer. This will limit the possible reuses. This is true for javafx dependencies as well as for AWT objects such as Point / Rectangle which are often used (wrongly) in model classes.

The reason is simple: Your code will limit itself to platforms and frameworks, Android for example does not support any of the mentioned java UI layers such as awt. Also, ORMs may have trouble with such types which would require custom adapters for your domain objects.

I have discovered that using a slightly modified version of the MVVM pattern also works well with javafx. In your example, you would design your model with a normal List and appropriate propertychange events. The ViewModel will then work as an Adapter for your Model, providing an ObservableList where the view can bind to.

  • Using *.fxml views: You have to use the javafx controller, which will simply create the binding to the ViewModel
  • Using code to generate the views: Just add a constructor dependency in your view to the desired ViewModel and bind in the view to the properties.

ViewModels often contain some boilerplate code, which you may want to avoid. However, the VM gives you also the opportunity to do some magic, i.e. using reflection to generate those List + Events to ObservableCollection automatically.

One last word about MVC: Swing as well as javafx are not designed to be used in a MVC way (since the controller is merged into the view). MVC is good for Components, where as MVP and MVVM are better suited for applications.