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 setItems
method, not only will the content of the list be shown in the ListView
Gui component, but also if items are added to the ObservableList
instance afterwards, the ListView
will be refreshed automatically and will show the newly added items automatically, without the need to call any additional add
or refresh
methods.
So far, so good. But what if I add something to the original list (i.e. list1
). These changes are not propagates automatically. It makes perfect sense, but it is inconvenient sometimes.
Of course, in a classic Java application the Model of the application does not consist of ObservableCollection
instance. So, whenever you add something to the model, you will always have to update the ObservableLists
instances that were derived from the original list. Apparently this is inevitable, right ?
This got me wondering, is it a clever idea to modify Collection
type occurrences (e.g. List
, Collection
, Set
, Iterable
, ...) in the Model classes and replace them by their ObservableCollection
alternatives from now on ?
Until now I always figured that these ObservableCollection
classes were only supposed to be used in the Gui layer of the applicaiton, but it seems pretty convenient to use them about everywhere.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With