BioJava:Tutorial:Changeability, Mutability and Events
Tutorial
BioJava contains a powerful API for communicating when objects wish to
change their state, and potentialy preventing them from changing if it
would invalidate the state of another object, all without violating the
principals of encapsulation. The main classes are in the
org.biojava.utils package and include Changeable, ChangeEvent,
ChangeListener, ChangeType and ChangeVetoException. For full
descriptions of all the API used here, please consult the JavaDoc API
documentation (latest biojava
1.8).
What is the difference between Changeability and Mutability?
Many Java objects are mutable. That is, you can invoke methods that
change their state. The Collections API supplys mutable implementations
of the List interface. There is also a method
Collections.immutableList(List l) that returns a view of the
underlying list where the mutators throw exceptions. Through this view
object there is no way to edit the list. However, if the underlying list
is modified then the ‘immutable’ view will reflect this. That is,
although it is immutable, it is still changeable.
Things get even more complicated in the world of bioinformatics. Many instances need to be mutable with respect to some clients and immutable for others. Also, some processes rely on objects remaining constant throughout. You can’t perform a database search reliably if the database is being modified. However, once the search is complete there is no reason not to change the database. This transient immutability can’t be modeled using the design pattern used for the collections. The situation above is complicated even further because while a search is going on, every single sequence must be maintained in an uneditable state. However, a search object realy doesn’t want to go through the process of modifying every single sequence object. This would be very ineficient. Something more flexible is needed, and the Changeability API is it.
What is a ChangeEvent?
ChangeEvent extends java.util.EventObject and adds the methods:
getChange- the new valuegetPrevious- the old valuegetType- the ‘type’ of eventgetChained- an event that caused this event to be fired
In constrast to the classical Java events model, one event class is
shared among all types of BioJava events. The ‘type’ of the event is
signaled by the value of the type property. ChangeType is a final
class. Each interface that will fire ChangeEvents will have
public static final ChangeType fields with descriptive names.
ChangeEvent objects store a descriptive name but are always compared
with the == operator. This scheme is a type-safe extention of the
Swing PropertyChangeEvent system but BioJava interfaces explicitly
publish what types of event they may fire.
ChangeListener: The contract for handling events
Objects that wish to be informed of change events must implement the
ChangeListener interface. This has just two methods:
preChange(ChangeEvent ce)postChange(ChangeEvent ce)
An object will invoke preChange to inform listeners that it wishes to
alter its state. A ChangeListener may fire a ChangeVetoException to
prevent this change from taking place. The event source must respect
this. Once the event source has finished updating its state, it will
invoke the postChangeEvent method with an equivalent ChangeEvent
(one with the same values for its properties). The postChange method
should then take appropriate action to update the state of the listening
object.
There are two ChangeListener implementations supplied by default.
ChangeListener.ALWAYS_VETO always throws a ChangeException in
preChange. This object is useful if you wish to unconditionally lock
an object’s property. In the exceptional circumstance when
ChangeListener.ALWAYS_VETO is registered and a postChange is
reached, it throws a NestedError with an assertion failure message.
This should only be able to happen if the event source is incorrectly
implemented.
ChangeException.LOG_TO_OUT prints all changes out to System.out. If
you want to log to a different stream, construct a new instance of
ChangeListener.LoggingListener with the stream.
Using ChangeSupport to implement Changeable
To flag that an object is a source of change events, it should implement
Changeable. This interface has the following methods:
addChangeListener(ChangeListener cl)addChangeListener(ChangeListener cl, ChangeType ct)removeChangeListener(ChangeListener cl)removeChangeListener(ChangeListener cl, ChangeType ct)
The methods with ChangeType arguments register the listener for that
type of event only. The methods without register the listener for all
events. Wherever possible, the type of event should be specified. This
potentialy allows for lazy instantiation of various resources and will
result in fewer events actualy being fired.
ChangeSupport is a utility class that handles 99% of the cases where
you wish to implement the Changeable interface. Idealy, you should
instantiate one of these objects and then delegate the listener methods
to this. In addition to the methods in Changeable, ChangeSupport
supplys the methods:
firePreChangeEvent(ChangeEvent ce)firePostChangeEvent(ChangeEvent ce)
These methods invoke the preChange and postChange methods of the
apropreate listeners. firePreChangeEvent will pass on any
ChangeVetoExceptions that the listeners throw.
AbstractChangeable is an abstract implementation of Changeable that
delegates to a ChangeSupport. In the cases where your class does not
have to inherit from any class but must implement Changeable, this is
a perfect base class. It will lazily instantiate the delegate only when
listeners need to be registered.
In the next tutorial, we will implement an event source and add some listeners to it.