Observables are one of the key abstractions provided by JFace Data Binding and are an implementation of the Observer pattern. They provide a common abstraction for observing changes in objects.
Core Interfaces of Interest
- IObservable - super-interface of all observables, allows to listen for generic change events
- IObservableValue - has getValue(), setValue(), and allows to listen for value change events
- IVetoableValue - inherits from IObservableValue, and allows to listen for before-change events
- IObservableCollection - extends java.util.Collection and IObservable
- IObservableList - extends java.util.List and IObservableCollection, and allows to listen for incremental list change events
- IObservableSet - extends java.util.Set and IObservableCollection, and allows to listen for incremental set change events
- IObservableMap - extends java.util.Map and IObservable, and allows to listen for incremental map change events
Implementation Design Principles
- An observable must remove listeners on the observed when disposed. For example if you registered selection listeners on a widget when the observable was constructed it must remove these on dispose of the observable.
- An observable should fire change events when the value changes if at all possible. This means when the value changes in the object being observed or when the value is set on the observable. One thing to look out for is firing multiple change events when this occurs. A use case where this arises is a widget that fires change events when the value is set programmatically (e.g. Text.setText(...)). In these use cases an 'updating' flag is normally employed.
- Not all observables fire change events when the state of its underlying object changes, but they do fire a change event if the change goes through the observable's setter. An example of this is Control.setEnabled(...) in SWT. If the object being observed doesn't fire change events when the value is set programmatically the observable cannot observe programmatic changes in the observed object. In these use cases it still pays to have the abstraction for get/set value but when coding against such observables it's good to be aware of this behavior. Because of this it's good to always set the value on the observable to ensure that change events are fired.
- In an observable when retrieving the value always retrieve it from the observed object. Don't cache the value in the observable if possible. Caching can cause issues if there's potential for this state to get out of sync like in the Control.setEnabled(...) use case mentioned above.
- If the observable is an
IObservableValue and the type of the attribute is a primitive use the primitive class (e.g. Boolean.TYPE or boolean.class) rather than the boxed type (e.g. Boolean.class) even though when get/setValue(...) is invoked the boxed type will be returned. By using the primitive the observable will be able to convey that the value cannot be null. This allows for better control in the validation and conversion phases of binding.