This article is in response to an excellent observation made by Martin Fowler here:
- Humane Interface - design the interface so that it's really easy to do the common case
- Minimal Interface - to the smallest reasonable set of methods that will do the job
This articles covers how to use Builders and Utility classes to make geotools easy.
Minimal Interfaces for Interoperability
As discussed elsewhere, geotools makes use of Interfaces and Factories everywhere. To ease the burden on implementors we try for the minimal set of methods that will do the job. We also aggressively take this approach with standards, fostering off definition (and associated careful documentation duties) on the GeoAPI project whenever we can.
So they don't line up yet, gives us something to work towards (or away from).
Violating Minimal Interfaces
What happens when you step away from the minimal interface? Let's consider the ones marked as "Funny"
To start with these methods involve creation, they are out of place in what is otherwise a API focused on functionality (ie capturing what is needed for interoperability). This places a burden on implemententors that is rather heavy - creation is a difficult problem!
To wit: the implementation will need to keep the Factory that created it - so it can implement these methods.
But what about Usability
It is true that the resulting interfaces are sometimes crazy to use (try making a CRS by hand, or changing something in a FeatureType). You will go crazy tring to do it once, you would be crazy to do it twice.
To address this need we have our utility classes:
Right now most of these utility classes consist of static final methods that do the right thing.
What about Builders?
The use of Builders is closely related. These, by definition, are classes that put a pretty face on creation. As such they often provide a humane interface beyond that available through a the minimal API required for interoperability.
Here is how Builders are different: they are used specifically to ease the creation process. Often they appear as a pretty mutable face on things that are hard to construct or immutable. StringBuffer is a builder of Strings, FeatureTypeBuilder is a builder of FeatureTypes. Builders that construct complicated content in this manner are stateful.
Going forward with Utility classes
One of the things that is happening as we fix up the toolkit and make it consistent is that these classes are moving away from being full of static final methods....
Why you ask? Because it turns out most of the methods actually need to do something and to do something you need to be connected up with GeoTools factories, a possible open-ended set of factories...
So lets look at the SLD utility class as an example.
This is a good example because:
- It shows using a Builder to aid in construction of something difficult
- StyleBuilder itself makes use of two Factories: StyleFactory and FilterFactory
This is a bad example because:
- StyleBuilder does not actually hold state (it only consists of "nicer" create methods)
- as such it is probably misnamed
Chaining utility classes together like this allows us to minimize the need for explicitly showing a cascade of factory injection.
The practice makes no difference to whoever is setting us up, and even represents less work for them.
To make SLD play nice with others we need to ensure that others can play with SLD. Playing is called dependency injection (SLD will depend on them to do some work after all). Setting this up is done in two ways.
You may have noticed that the above would of broken a lot of code - here is the missing link:
This preserves backwards compatibility, it is deprecated (so it will be removed in a future release). It does allow a hook for the default to be forced by client application code, but since this cross cuts the entire VM it is not recommended.
GIS is a tough nut to crack, often the task is to make things work at all. If you get a chance try and provide assistance to those using the toolkit. This article illustrates how this can be done without consequence to interoptability.
An alternative approach would be to put all the fun convenience methods into the implementations; this would be dangerous if we accidently started using them internally - and it would have the negative consequence of locking users into a specific implementation.
The relationship between minimal interfaces and usability is a difficult balance, the use of utility classes can act as a wrapper around the problem. Wrap up an implementation with a utility class for ease of use, just be sure to allow for the network of required factories.
In the explicit case of creation Builders often allow for a more Humane (and sometimes the only) take on the situtation.