The Observation API (hey, it's not the Observable pattern)
As I said in
I'm going to post on my blog a series about the use of Semantic
Technologies and how they are being used in some of my projects.
Today let's start giving a small domain model and then design a related
abstract API. In the next post I'll show you how to
implement it on the top of a
RDF triple store.
API is designed to manage a set of "observations" (of whatever, by
anybody, etc... let's keep it generic in order to respect the "AAA
slogan": Anyone can say Anything about Any topic), so I'm
it "the Observation
Let's introduce the key abstractions:
- An Observer
represents entities capable to make observations. It may be a
physical person, or a device, or anything else makes sense.
- An Observation
is made at a certain time and Location,
by one or more Observers
and is composed of one or more ObservationItems.
- An ObservationItem
pairs an Observable
with a Cardinality.
- An Observable
has no special properties and can be anything.
- A Cardinality
may be a single integer number, a close or open range of integers (e.g.
"between 10 and 20", or "more than 5"), or "undefined".
- An ObservationSet
is a set of Observations.
- A Source
is where a certain datum comes from (i.e. who or what provided it,
inserting into the database).
All of the above concepts are going to become classes, so I'm now
referring to them with the code typo convention. style="font-family: monospace;">Observer,
have a single property exposed through style="font-family: monospace;">getDisplayName(),
that is the identifier used for rendering the object in a UI. Applying
some common patterns, we add a few more classes for the solution model:
style="font-weight: bold; font-family: monospace;">ObservationManager
style="font-family: monospace;"> provides way
to retrieve and create ObservationSets.
style="font-weight: bold; font-family: monospace;">Finder
is used to perform various queries.
style="font-weight: bold; font-family: monospace;">Observation.Builder
provides ways to create new Observations.
only architectural dependency introduced in the API is related to the
use of components Ã la NetBeans Platform, whose lookup logic is hidden
behind a ObservationManager.Locator.
has been recently introduced for dependency injection in
the Platform, this class will be removed at some point in
making the API mostly technology-agnostic.
href="https://bluemarine.dev.java.net/nonav/Blog/20090429/it.tidalwave.bluebill.observation.png"> style="border: 0px solid ; width: 800px; height: 638px;"
(Click on the above diagram for a larger version)
Scenarios and examples
I have anticipated that this API is very generic, for the following
scenarios I'm giving the context of birding (or birdwatching) - which
BTW is the reason for which I've designed it.
First of all, code dealing with the Observation API shoud create an style="font-family: monospace;">ObservationSet:
ObservationManager observationManager = ObservationManager.Locator.findObservationManager();
ObservationSet temporarySet = observationManager.createObservationSet();
ObservationSet persistentSet = observationManager.createObservationSet(backingStore); // backingStore is a generic Object, architecture dependent
provides a convenience method findOrCreate()
for creating various entities of the API, even though they can be also
instantiated in other ways - let's remember that things such as style="font-family: monospace;">Observer, style="font-family: monospace;">Observable, style="font-family: monospace;">Location and style="font-family: monospace;">Source are
pretty generic stuff, and their implementation could come from some
other APIs. This is a
very important point: in a real application I don't expect
to be really used alone, if not for ancillary, simple entities, because
real world I'll mostly need some concrete entities with their own
properties and behaviour. But the simple use of this method is ok for
tests (and examples).
Here it is how I could create a new style="font-family: monospace;">Observation of
about 100-150 flamingoes and 23 spoonbills:
Date when = dateFormat.parse("29-04-2007");
Location castiglione = observationSet.findOrCreate(Location.class, "Castiglione della Pescaia", EmptyInitializer.instance());
Observer fabrizio = observationSet.findOrCreate(Observer.class, "Fabrizio Giudici", EmptyInitializer.instance());
Observable flamingo = observationSet.findOrCreate(Observable.class, "Flamingo", EmptyInitializer.instance());
Observable spoonbill = observationSet.findOrCreate(Observable.class, "Spoonbill", EmptyInitializer.instance());
Observation observation = observationSet.createObservation().
item(flamingo, Cardinality.rangeOf(100, 150)).
Note how a fluent interface has been used for specifying all the data
items for building an Observation.
If this operation is made against a persistence style="font-family: monospace;">ObservationSet,
the inserted data are made persistent too (eventually at the next
transaction commit; details are opaque to the API). The only required
parameter for findOrCreate(),
besides the entity type, is a string which represents
name of the entity; if an entity of the given type and display name
exists, it will be returned and not created. A third argoment is an
implementation of the interface:
public interface Initializer<T>
public void initialize (T entity);
is responsible for initializing the entity status if it is created from
scratch; this will be discussed in a future post, in the meantime you
just need to know that EmptyInitializer.instance()
returns a "no op" initializer that does nothing.
Now, how to perform queries? The starting point is the style="font-family: monospace;">ObservationSet
returns a Finder
which encapsulates the query logic. It also provides a fluent interface
to specify some generic (optional) parameters of the query; in the
above example the sort criterion and the "paging" of results (from the
100th, for a max of 20 items), which is easily integrable in user
interfaces supporting paging. You can specify criterion for a few
generic attributes of an Observation
DATE, style="font-family: monospace;">LOCATION, style="font-family: monospace;">OBSERVABLE).
There are no more things to specify here as we are still dealing with
Finders are used in other places of the API too: for
instance, you can do:
observable = ...;
finder = observable.findObservationItems();
Location location = ...;
Finder<ObservationItem> finder = location.findObservationItems();
that is, you can query single entities for the related concepts.
Enough for today. Some resources: