Wrapping a Back-end Service
The initial aim of Uncle Unc was to provie a shared interface to a range of network resources such as databases, LDAP directories and jini networks. This sort of Enterprise Integration is still a major focus of the project.
From an object-modelling point of view there are two ways to map a back-end service to Unc
-
create a specific subclass of Item or View, and initiate the mapping in the setReference() method, when a suitable object (e.g. a java.sql.Connection for a database browser) is passed.
-
create a bespoke Shaper class that takes the reference to the back-end service, and adds properties, methods and children to a Item or View class of any type.
The first approach is somewhat simpler, but less flexible, as it enforces a one-to-one mapping between Item and back-end service. The best way to illustrate that problem is by example.
Take the case of a text file containing XML markup of some sort. If we encounter the file in a filesystem, we will initially wish to add methods and properties appropriate to a file (size, name, last modified, etc.). So we define a FileItem that extends Item.
As an XML document, we may wish to represent the breakdown of nodes within the XML tree as a tree of Items. So, we can define a XMLFileItem that extends FileItem. Here we hit our first problem with inheritance - we may obtain an XML document from any InputStream - from a URL or a zip file entry, say, neither of which should be represented by a FileItem.
Let's continue anyway. Suppose the XML file is an Ant build file, or a server configuration file of some sort. We may wish to also provide access to the XML data at a higher semantic level than mere nodes and attributes. So, using inheritance, we define an AntFileItem, extending XMLFileItem. Our inheritance tree is quickly becoming complicated. And wait! It's also possible to construct an ant project programmatically without parsing any XML, but our AntFileItem can't deal with that eventuality.
(This raises a second issue too - when we open the View do we want to see XML node children? Or ant target children? Or both? Using shapes doesn't solve that directly, but use of default methods does.)
If we use Shapers, we are using the well-known 'decorator' pattern. We do not need to define any subclases of Item, rather we define three independent Shapers, one for files, one for XML documents (from whatever source), and one for ant projects (from whatever source). If we find an ant build file, we can apply any or all of these Shapers to it, in order to get the representation of the resource that we want.
In our example, we probably don't want to add XML nodes and Ant nodes all at once, but in other cases, we might. Taking the example of messaging, it might be very useful to have a single View being simultaneously shaped by a mailbox, rss feeds, an IM client and so on.
The codebase is currently being migrated from an Inheritance-based approach to use of Shapers. Currently (April 2003, build 0.25.1) object reflection and the filesystem have been fully ported, but the old inheritance-based clases are still present as well.