|
|
|||||||||||||||||||||||||||||||||||||||||||||
Jody Garnett's Blog
Minimal vs HumanePosted by jive on December 08, 2005 at 12:18 PM | Permalink | Comments (0)I recently wrote an article on the GeoTools website responding to the Minimal/Humane contrast between Java and Ruby. Fron the intro This article is in response to an excelent observation made by Martin Fowler here:
This articles covers how to use Builders and Utility classes make geotools easy. Here is the link to: Pain Free GeoTools I often find my writing is better situtated closer to the community then hidden off on a blog somewhere, I will try and make sure I use this as a bit of a feed to bring attention to my public writing. (rather then just a source of origional content). Suporting Hacks and VersionsPosted by jive on November 13, 2005 at 09:14 PM | Permalink | Comments (0)This is Part II of two previous articles! I like to multitask ... and a good designer knows when resuse is possible! With respect to XML Standards, in Part I we talked about ways of representing XML Standards using Objects, now we take on a hard problem - taking on an update to a standard! The solution is general in nature and will let you take on any changing API, and indeed support multiple versions at the same time. With respect to the use of Interfaces and Factories, this article serves as a follow up to the use of Factories, and represents the kind of neat stuff you can get away with in a well designed system. Architecture Astronauts AlertIf you have a low BS tolerence tune out now, if you want a hint at how geotools plans to support the next big thing - that is easy we write it! It is the next small thing that has me worried - a lot of the services we use are changing on us, and they are changing versions. How are we to write classes that support both cool shiny new stuff (tm) and last years fashions? And is there a difference between hackers and the spec writers? Does the tail wag the dog? Here are some specifics that got me thinking:
FooBar 1.0To start with lets take a simple example: interface FooBar { boolean isGood( Geometry geom ); } And we can assume the usual suspects: FooBarFactory and FooBarFactoryFinder. Lets say some time passes and a new version 2 comes out that has support for morals, morality is given a range of 0-1 (we are comuter scientists after all). We would like to add the following: interface FooBar { boolean isGood( Geometry geom ); /** @since version 2. */ double getMorality(); /** @since version 2. */ void setMorality( double moraility ); } But if we do that we lose something .... Using JavadocsIf we make the above change we are now dependent on programmers to read the javadocs, and we all know how well that goes. I tend to use geotools as a binary and hope for the best, especially given the source code releases. Lets look for something better. Using InterfacesWhat I would really like to preserve is Strong Typing so the compiler (not me) can worry about the different versions. interface FooBar implements FooBarVersion1, FooBarVersion2 { boolean isLabelShield(); } interface FooBarVersion1 { boolean isGood( Geometry geom ); } interface FooBarVersion2 { double getMorality(); void setMorality( double moraility ); } This approach makes everything explicit, and allows for fun little non standard geotools hacks (like LabelShield above). We have a number of benifits, code that is really limitied to Version 1 can say so with FooBarVersion1 interfaces. Code that does not care can track the FooBar baseline. Seems like a winner to me ... Using FactorySo what is my hesitation? Usability lets look at how this plays out, and see what we can do to make it worthwhile... We can make explicit factories for each version: interface FooBarVersion1Factory { FooBarVersion1 create(); // default implementation } interface FooBarVersion2Factory FooBarVersion2 create( double morality ); // default implementation w/ custom morals } Our default use of Factory should still work: interface FooBarFactory extends FooBarVersion1Factory, FooBarVersion2Factory { FooBar create(); // default implementation FooBar create( double morality ); // default implementation w/ custom morals } Is it Good?So we should be able to answer the question - is this a good design idea? Lets see how we do? FooBar hacker = FooBarFactoryFinder.create( 0.9 ); // hackers need a bit of flexability FooBarVersion1 greenshag = FooBarVersion1Finder.create(); // They were going to get it right the first time FooBarVersion2 black = FooBarVersion2Finder.create( 0.98 ); // They are pretty sure they got it right Seriously, this can work. It will let me support Filter 1.0 and Filter 1.1, SLD 1.0 and SLD 1.1 and by extention we can make strongly typed interfaces for WFS1.0 and WFS1.1 clients. I want to balance:
By extention our GeoTools interfaces (and implementations) will need to step back from the standards and be bigger then them. For examples of how to do this look at the WebMapServer implementation that Richard Gould put together. Transition from Objects to Interfaces to ContainersPosted by jive on November 13, 2005 at 08:46 PM | Permalink | Comments (2)GeoTools loves its interfaces - this drove me mad when I first started using the toolkit. One of the things about BEFORE (aka classes):public class Foo { public Foo( int magic ){} public void doSomething(){ System.out.println( "boo!"); } } AFTER (aka interfaces/factory):public interface Foo { public void doSomething(); } public interface FooFactory { public Foo createFoo( int magic ); } Why would you do this? Because you want to work with others - include people you have not yet met! The idea is someone can come along and give you their own FooFactory and all of a sudden your code is doing new and wonderful things without evening noticing. Now the alert will notice there is a problem here? How the heck do you make a FooFactory? A FooFactoryFactory seems a bit silly ... lets work on bootstrapping the process. BootStrapping FactoriesUsing a Plugin System (and Defaults)I have to continue with my above example for a bit ... public class FooFinder(){
FooFactory[] list();
}
... the interesting thing here is that this class really is magic. This is really the front end to the GeoTools plug-in system, we change the technology behind it every once in a while (but these FooFinders will always work). Our usual goal for shopping for a plug-in system is this: dropping a jar on the classpath and having more FooFactory instances show up in that above list. Now we are usually a bit kinder then that and include some utility methods: public class FooFinder(){ FooFactory[] list(); FooFactory defaultFactory(); Foo create( int magic ); // create a Foo based on the default factory } How and ware the defaults come from, and even the order of that origional list is all kind of a mystery to me. The good news is it rarely matters. The exception to this rule is with EPSGAuthortyFactories, I end up controling the process by only including one the jars on the classpath that I am interested in, but that is a lo tech answer. Lets see this in use: Foo foo = FooFinder.create( 3 ); foo.doSomething(); That was not too bad, and we can manage to work with others - just as long as they are setup as the default. Using a Plugin System (and Metadata)The other thing to do is use the plugin system and wander through that list and make an intelligent choice. We have to get the factories to tell us something interesting to make that choice, and that something is called (cough) metadata. This time I am going to go for a bit more of a realistic example - Bar (okay not that much more real). interface BarFactory { String getName(); String getDisplayName(); String getType(); Bar create( URL url ); } Now when we use BarFinder we will have something to talk about. for ( BarFactory factory : BarFactoryFinder.list() ){ if( "good".equals( factory.getType() ) ) return factory.create( URL ); } throw new FactoryException("Could not find a good factory"); Note we also have getDisplayNames to show to users, and getName to write in configuration files. Fun. This is still a little bit backwards, it involves us knowing ahead of time (or at least asking a user) interface BarFactory { boolean canProcess( URL url ); Bar create( URL url ); } Now when we use BarFinder we will have something to talk about. for ( BarFactory factory : BarFactoryFinder.list() ){ if( factory.canProcess( url ) ) return factory.create( URL ); } throw new FactoryException("Could not process "+url ); The fun part about all this is that we can use the plugin system to grow the capability of the geotools over time. Using A Factory as a ParameterI hope it is mostly clear how to use a Factory yourself, another thing you can do is pass a factory off to other code to get them to use it. Foo parse( File file, FooFactory factory ); The factory is used when the parser needs to create a Foo instance to return to you. This is really nice as it allows you the user to use existing parsers to produce your own objects. AbstractFactory and the Pattern Police This is especially interesting with Factories that produce a varity of content that designed to work together. interface Math { Number number(); } interface Factory { Math literal( Number number ); Math add( Math a, Math b ); Math sub( Math a, Math b ); Math mul( Math a, Math b ); Math div( Math a, Math b ); } This is often called an AbstractFactory by the GOF(aka Patterns Police). You will see the GOF uses these with a Builder pattern to make interesting data structures like expressions and documents. Using a Factories together!So the question becomes what of a Factory that uses another Factory? Annoying it is actually very common. In GeoTools we have the following chain:
The only simplifcation we currently have (by accident) is that FeatureType is the FeatureFactory! There has to be a better way! Using a ContainerThat still seems like a lot of bother, you have to talk to two classes A Finder and a Factory before you ever get anywhere ... there has to be an easier way. And there is A container is a strange offshoot of the J2EE scene. The formal definition is something along the line of a "blackboard that supports lifecycle" or something like that. Lifecycle being alhpa and the omega of an Objects existence. A blackboard is a nice architecture that lets code loosely collaborate on stuff. Justin will probably write us up a container article real soon now ! Mechanics of Setting up a Factory for Container UseI am just going to jot down some notes on how a Containers formalize Factories working together. Oh they call this "dependency injection" and the whole process "Invrersion of Control" (IoC) - I am going to let Justin explain that one. Because of this formalization containers can do all sorts of magic that make this stuff easy to use and easy to write. Constructor InjectionThis is the easiest to understand: public class GeometryFactory { public GeometryFactory( CoordianteSequenceFactory coordinateFactory ){ ... } } There is a problem with that - it a class not an interface. Easy and straight forward though. Setter InjectionAnd this is also straight forward: public interface GeometryFactory { public void setCoordinateSequenceFactory( CoordianteSequenceFactory coordinateFactory ); } Indeed you can do both at once. Using a ContainerJustin is going to have to look at this , but I thought I should at least provide the flavour of what a container offers you as a user. Update - Justin came through: Now back to our nice code examples:Parser parser = container.get( Parser.class ); Feature feature = parser.file( file ); Where are all the factories? Well in the container somewhere, I am assuming Justin set it up based on all the things we mentioned before. Sigh you don't belive me lets do it by hand: container.add( MyFeatureTypeFactoryImpl.class ); container.add( MyGeometryFactoryImpl.class ); container.add( MyCoordinateSequenceFactoryImpl.class ); container.add( MyFeatureFactoryImpl.class ); And after that .... Parser parser = container.get( Parser.class ); Feature feature = parser.file( file ); Note: we only put in classes, it will construct instances of those factory classes and configure them using constructor or setter injection before constructing that parser for us to use. Why would you go to all this trouble? Well check out the next posting ... Supporting Hacks and Versions!Where in the world?Posted by jive on October 07, 2005 at 08:51 PM | Permalink | Comments (1)Sonny Parafina recently derided the state of extent information in the free data available on the web. Let's see what can be done despite the data providers. One of the great turning points of the web (yes I remember) was when we stopped trusting data providers. Seriously keywords were only filled out by those seeking to mislead you, or a few keeners. Spatial data today is in the same boat - 90% of the searches you do for anything turn up the "Important Bird Areas". Why is this .. because the provider of that data got annoyed, so annoyed he actually filled in all his metadata (and keywords), and now shows up all the time. Search for water? You get information about water birds, search for Canada, you get information about our wonderful Owl population.
This is silly, this is the web of 1993. Only now we have maps. As talked about in a previous blog, the big three search engines are just cottening on to the power of location. It will be a bit before the start to help us with this problem. I should mention the nice email that started this thinking (from Sonny Parafina):
I agree - lets see what can be done.... but first some background information. How is Spatial Data PublishedSpatial clients often work directly with a single URL (this is called a Capabilities URL, and the result is a GetCapabilities document describing the service, and the data provided). It is this document that is often inaccurate, or more specifically not usefully accurate. Clients use this Capabilities Document as a "table of contents", in addition to providing information about available data it also tells you what URLs can be used to obtain the data. As an example Web Map Servers will have an entry for their GetMap request. Can we trust Data ProvidersUm - apparently not. Seriously data providers are kind of done at the point where they have collection information. They know what it was good for, we are kind of lucky they decided to publish it at all. One thing we can do is go back the the Yahoo of old, filled with hapless people looking over the pages and treating everything like a library. Yes - a manual process may tide us over until something smart comes along. It is after all how this stuff is cataloged right now ... Back to Sonny's email:
That is right he is hitting a Refractions service for this information, and that is constructed by a series of targeted searches of good old google . Yahoo for Spatial InformationAnd this is the idea - construct a "metadata look-aside service". One that all us client apps can consult for a second opinion, basically it would serve up user supplied corrections for specific WMS/WFS/WCS Layer information. There are some technical difficulties, these standard based web services are not the best at marking their generated content with the appropriate HTTP modification information, and the standards themselves only recently started sticking in generational version information (so you can tell when the document contents changed). Still we need to think of something to trust. If not the capabilities document itself ... perhaps we should just record the original bounding box and the correction. As long as the service is showing us the original value we will know that they have not fixed the problem ... and our correction is still "good". Actually lets not say good lets say "better". Social hacking time - why would people do this? At least the users of data have an interest in fixing the problem (by definition) and would probably like a way to "fix" the data (by the convention of using this look-aside service). How about the server providers? If we record the number of hits to this look-aside service we could at least provide the data providers with a figure of how many users they are annoying, along with an email of the fix ... that is not bad. The alternative is giving users a "you suck" button set up to email based on the contact information. Oh wait we can't trust that either ... This idea does have a draw back, some WMS server providers (using software by a nameless commercial vendor) have an annoying habbit of "Cascading" layers. This means that the same (possibly wrong) information will be trickled around the web.This look-aside service would annoy those with cascading WMS servers, as they would get bothered about extent information they have no direct control over. But then again they annoy me (often by removing their cascade layers the moment there is a problem, and then pretending they never heard of the information they knew a few moments before...). Still there is something here ... fight fire with fire. Cascading CorrectionsRemember how spatial data is published? Those are XML documents and can be hacked by the daring.Since these are well understood formats, we could make our look-aside service take the URL to the capabilities document. If it is known it can patch the document as it is returned, if it is unknown it can pass it through unmodified ... So how about some social motivation? This does sounds like a great way to lazily generate a catalog of layers ... so a catalog provider should have motivation. How about data users, why should they supply the corrections? Well they do use the data, and this information does add value to the data making it more useful to them - that is a good trade.Any more technical issues? A few clients would not enjoy the experience. Very early clients expect to hack at a single URL and provide different request parameters. That is they don't expect a separation between entry points for the capabilities document, and getMap requests, etc ... I am pretty sure we have disappointed that assumption often enough on the wibbly wobbly web by now that these issues have been fixed.
Sounds like we have an idea? Any takers ....
|
December 2005
Search this blog:Archives
December 2005 Recent EntriesTransition from Objects to Interfaces to Containers | ||||||||||||||||||||||||||||||||||||||||||||
|
|