A Better Data Binding Model
Join the DZone community and get the full member experience.
Join For FreeIf you have ever implemented a user interface, you have more likely been confronted with the problem of data binding and notification: whenever a value in your model changes, how do you update your user interface to reflect the new values?
In Java, the standard solution is to use PropertyChangeSupport and PropertyChangeListener, but I have always found this approach very cumbersome and hard to scale. In this post, I will explain why and I’ll offer what I think is a superior solution.
The problem with PropertyChangeSupport
Here are the main issues with PropertyChangeSupport:
Extreme boilerplate
Any bean or model class that wants to notify the rest of the world that its value changed needs to include the following:
public class SomeBean {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public void setProperty(String newValue) {
String oldValue = property;
property = newValue;
changeSupport.firePropertyChange("propertyName", oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changeSupport.add(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changeSupport.remove(l);
}
}
That’s a lot of code for something that should be very simple.
Note that this code looks like it might be a candidate for a trait (the kind you find in Fantom or Scala). A trait (or mix-in) is a class with several restrictions that other classes can “import” directly as part of their implementation. If we defined a PropertyChangeSupport trait, any class importing this trait would automatically expose the three methods defined above.
The problem is that most trait implementations impose certain restrictions on how you can specify your trait, and one of them is that they can usually not carry state (no fields). Since a property change support will usually carry at least one if not two pieces of state (the change support object and maybe the name of the property), I’m not convinced that traits would be useful here. If anything, only languages that support multiple inheritance of implementations would allow some of this boiler plate to be reduced (e.g. C++).
Extreme coupling
In order for a GUI element to update itself when a value on the model changes, it needs to add itself as a listener on that model. For example, you can imagine that if a name changes in the mode, the user interface will want to update a label and a window title. If you do the math, you will see that “n” user interface elements that need to monitor “m” model objects will result in n*m couplings. And of course, your model and your view are now very tightly coupled, which adds even more problems.
Extreme data exposure
Because Java doesn’t natively support properties, not only do you find yourself having to specify your setters manually, you also need to remember to fire the appropriate PropertyChangeEvent whenever a value changes. This is not only error prone, it also exposes you to the dilemma of deciding whether you want to fire such events for model objects that don’t directly impact the GUI at the moment (what if they do at some point in the future?).
Epiphany
All these considerations started bugging me quite a bit recently as I was busy working on an Eclipse plug-in. I was multiplying the number of PropertyChangeSupport objects throughout my models and mostly extending the same class over and over (if I was lucky) or just copy/pasting the code above (most of the time). I strongly disliked the amount of pollution that was beginning to invade my code and I started considering alternatives.
And then, I remembered a post that I wrote a few months ago about “local buses”. It occurred to me that if I could insert a message bus between the model and its listeners, then the two worlds would become completely decoupled. On top of that, I might be able to add some kind of property support that would hide most of the complexity.
JBus and properties
My implementation of the local bus is very small and very simple and is similar to what I described in the blog post linked above: listeners are methods and they declare themselves with a @Subscriber annotation. These methods are expected to accept one parameter which represents the class of the event they are interested in.
Here is a quick example:
public class App { @Subscriber public void event1(NotifyEvent ne) { System.out.println("event1: " + ne); } }
JBus mb = new JBus(); jb.register(new App()); jb.post(new NotifyEvent("notify"));
This is the first part of the plumbing. GUI elements that want to receive updates will simply declare themselves as subscribers for certain types of events (typically, PropertyChangeEvent, but it doesn’t have to be restricted to that).
The second part is to automate the publication of events as much as possible, and I ended up creating a class Property for this. Here is an example of how to use it:
public class Person { // A property that the GUI is interested in private Property<String> name; // A field that the GUI is not interested in private int age; public void setName(String n) { name.set(n); } }The first thing you notice is that because they are of type Property, properties that publish events are easy to identify in the code. Additionally, properties will automatically publish events whenever their setter is invoked, which imply that 1) they need to have an instance of the message bus and 2) they need to know the name of the property that changes will be published under.
Therefore, the Property constructor looks like this:
public class Property<T> { public Property(JBus bus, String propertyName, Object source) { ... } }
The source is optional but if you specify it, it will make it possible for events to be discriminated based on where they came from, which has several practical uses.
Benefits
As soon as I converted my code to using JBus and Property, I noticed a radical decrease in boiler plate code and a dramatic increase in flexibility. Whenever a new model appears, making it publish events is trivial. Whenever I introduce a new graphic element that needs to update itself, I no longer need to find a way to locate the model object(s) it’s interested in and add itself as a listener: all I need is the name of the property.
Limitations
Here are a few downsides to the JBus approach:
Bus ubiquity
In order for this approach to work, model objects and graphic user interface elements must have a pointer on the message bus used to exchange events. In effet, I am transforming an “m * n* dependency graph into an “m + n” one, with all m and n depending on the message bus. The bus is becoming the central piece on which all event entities depend.
Is this a bad thing? I’m not sure. From a design perspective, I still think that the general decoupling that results from the introduction of the bus is too flexible to ignore. From an implementation standpoing, having this kind of uber object present everywhere can be a concern but dependency injection frameworks make this a non-issue.
Another potentially interesting aspect that I haven’t explored is that you don’t need to restrict yourself to having just one instance of a bus. If certain parts of your applications are completely isolated from the others, you might want to create a specific bus just for them since this will speed up the look up and delivery of events. I’m not sure that the performance improvement would be noticeable in general, though.
The property name
While we have almost entirely decoupled models and GUI elements completely, they are still connected by two tiny pieces of information: the bus and the name of the property. The downside of this approach is that the coupling is no longer statically typed and if you ever change the name of a property, you need to make sure that all the subscribers get updated as well. I haven’t seen this as a big issue so far and you can always increase your comfort level by having these classes reference to the same string constant (thereby re-introducing a little bit of coupling).
If this is still too loosely coupled to your taste, you can always decide to have your GUI element bind itself to the Property directly. In this case, the GUI element no longer even needs to have an instance of the bus, but you are also getting dangerously close to the problem you were trying to solve in the first place.
Performance
JBus uses reflection to calculate recipients of event messages, which is probably slower than when your GUI objects add themselves as listeners of your model objects. I am planning to improve the performance of JBus in that respect (with a lot of caching since the reflection information will not change once an instance of the bus has been created) but you are also not limited to JBus. You can always implement your own concept of a software bus and make it work without any reflection at all.
In practice, my gut feeling is that the overhead caused by reflection will probably be dwarfed by the delays found in the GUI anyway, so I’m not too worried about that.
Potential improvements
While I started implementing JBus as a generic local bus framework, the fact that it’s so useful as a property change publication mechanism made me wonder if I shouldn’t provide some specialization for specific use cases, namely making special cases for PropertyChangeEvent, which is likely to be the even that is the most used.
For example, JBus allows you to publish events in certain “categories” (strings) and allowing listeners to only receive events if they have declared an interest in the type of that event *and* the fact that it was published in the same category. I noticed that it’s often convenient to publish property change events automatically assigned to the category that matches the property’s name.
Interfaces vs method listeners
The way JBus matches events to listeners is not very different than the way you do it with listeners, except that listeners defined in interfaces force you to implement the entire interface, even when you are not interested in all the lifecycle methods. The @Subscriber annotation allows you to only receive notifications for the events you are interested in.
In conclusion, data binding user interfaces to models with a message bus provides more flexibility and allows you to decouple your domain objects completely. I’m hoping that this approach will help me build user interfaces that are able to manage their consistency without as much work as the PropertyChangeSupport approach requires.
From http://beust.com/weblog/2010/09/26/a-better-data-binding-model
Opinions expressed by DZone contributors are their own.
Comments