Choosing a Model/Event Pattern
Join the DZone community and get the full member experience.
Join For FreeThe following diagrams show different implementation patterns of
models and corresponding events, as typically used in the UI. Depending
upon your needs, you can use the following tables to decide which
pattern to implement.
The patterns are split into model patterns and event patterns. Not all
combinations of the patterns make sense, but patterns can be combined
to fulfill your unique requirements. At the end I make some
recommendations.
Model Pattern 1 - Automatic Event Firing
With the above pattern every change to the model results in an event
being fired. Typically if the focus of a text field is lost, the model
gets set (in Eclipse RCP perhaps using databinding). Any views that
show this model attribute get notified and update themselves. Works
well for cases where multiple views of the same model need to be
continuously up to date. A big disadvantage is the number of events
which get fired. You also need to watch out for cyclic dependencies
whereby an event updates a second view, and that update fires another
event which updates the first. UI performance can suffer with this
pattern, because its granularity is simply too fine.
Model Pattern 2 - Manual Event Firing
This pattern lets the caller (typically the controller) fire an event
after it has finished updating all parts of the model. Typically the
controller performs any validation of multiple fields, may call the
server and when everything is good, updates the UI's model. An example
might be when an input dialog is closed and the data from that dialog's
model needs to be incorporated in one single sweep into the main model.
Advantages of this pattern are that events are only fired when all data
has been validated together and you can be sure all data is coherent.
The disadvantage with this pattern is that you rely on the caller to
fire the event (the developer must remember to write that code!). What
typically happens is that the same fire method gets called frequently.
When implementing this pattern, you can choose if you supply one "fire"
method which takes an enumerator, or whether you supply many methods,
one for each relevant level of granularity.
Model Pattern 3 - Coarse Grained Model Methods
In order to reduce the number of events which are fired by Model
Pattern 1, and reduce the problems of the caller having to fire events
in Model Pattern 2, the above pattern provides coarse grained methods
for updating multiple model parts together, in one "transaction". The
model implementation sets all the relevant parts based on the given
parameters, and only at the end fires a relevant or several relevant
events (in the above example, perhaps an event that some elephants were
added and an event that some hippos were added).
Model Pattern 4 - Model with Suspendable Event Firing
A different solution to the disadvantages of Model Patterns 1 & 2,
is to allow the caller to suspend event firing temporarily. While this
approach is interesting, its comes with dangers like losing events from
concurrent model updates. For example, a user event causes a call to
update a large amount of attributes. In the mean time, another user
event causes a single attribute to be updated, but because event firing
has been suspended as part of the first update, its event is never
fired. The solution is that when resuming event firing, a generic event
is fired, to tell all listeners that the entire model may have changed.
While this pattern builds upon Model Patterns 1 & 2, it still
suffers like Model Pattern 2, by relying on the caller to make a call
to resume event firing at the end. Again, a controller can encapsulate
these extra calls for resuming, so that they are at least all located
in a logical place.
Event Pattern 1 - Listener is told about granularity of event
In the above pattern, the listener is given a hint about what has
changed. The listener can then base its decision on what to refresh
upon this context. Better implementations don't use Strings, rather use
an object hierarchy, firstly so that the pattern becomes strongly
typed, and secondly so that tests of "implements" can be carried out,
so that the listeners implementation does not get filled full of "if"
or "switch" statements. For example, a specific listener may be
interested when any animal in the zoo is modified, whereby a different
listener may be interested in specific events like elephants and hippos
changing. With this pattern, the listener must have a reference to the
model in order to get the refreshed data and as such may not know that
only one item in a list has been changed, causing it to refresh more of
the view than necessary, as might be the case with tables where only
one row has changed.
Event Pattern 2 - Listener provides context specific methods
In this pattern, the listener is informed about fine or coarse grained
events, but does not get told about say individual rows in a table
which have changed, forcing the whole table to be updated. This pattern
is also less flexible than Event Pattern 1, because the implementation
cannot test for an events superclass. Effectively this pattern is like
Event Pattern 1 when using Strings, only it is strongly typed. As such,
Event Pattern 1 is more flexible.
Event Pattern 3 - Listener receives model parts - fine grained
With this pattern the listener is told exactly what has changed, which
has the advantage that the listener only needs to update exactly what
has changed, for example a single row in a table. This pattern
implicitly relies on Model Pattern 1, and is hence fine grained.
Event Pattern 4 - Listener receives multiple model parts - coarse grained
This pattern extends Event Pattern 3 by becoming capable of handling
multiple updates because the listener is given a number of changed
items at the same time. It might be associated with Model Pattern 2, 3
or 4, where multiple updates are performed before the event is fired,
however as shown, it is constrained because all changes need to be of
the same type.
In my experience Model Pattern 3 and Event Pattern 1 are the best combination because they are the most flexible and reduce the number of events being fired which improves performance and usability. In cases where it is important for the view to only update certain rows of a table, the listener methods can be changed to also pass the changed objects to the listener, however this is only required in cases where UI performance would otherwise be really poor.
I would like the thank Mike Moor and Vera Fischer of the SBB for their inputs.
Opinions expressed by DZone contributors are their own.
Comments