The Artima Developer Community
Sponsored Link

Musings on Language and Design
Using Groovy Builders to Simplify Complex Object Hierarchy Creation
by Jeremy Meyer
June 26, 2010
Summary
A closer look at how Groovy builders help with creational code in complex API's, using the Java3d API as a fun example.

Advertisement

This is not just a discussion about Java3d, it is a discussion about builders in Groovy and how they can ease our creational code. In my last blog, Java 3D Fun with Groovy, I showed an example of using the Java3d libraries via Groovy. The seamless integration with Java made it easy to use all of the Java3d API classes, but I whined that it was hard to create a 3d scene in code.

Actually, I should have been more specific. It was hard to create 3d scene in readable code, and it was hard to get an idea of the hierarchy of the elements of a 3d scene by looking at other people's code. This is not an unfamiliar problem, and one which Groovy has vastly improved with its builders. I want to illustrate them here.

Structure in Code

Even with a powerful API a lot of our time as software developers is spent just creating stuff. Data structures, domain classes, XML, GUI Layouts, Output streams etc. A recurring theme in the creation of all of these is the creation of an objects which contains objects. These objects might in turn have their own nested objects and so on. The problem with this, is that your typical programming language cannot know anything about the structure of our domain objects, and so our code looks nothing like our hierarchy. Say a customer object has an invoice object in it, or a teacher object has many student objects, when we are initialising our hierarchy, we invariably have to treat all of the objects as if they had the same hierarchical level.

Consider:
   Teacher t = new Teacher("Jones")
   Student s1 = new Student("Bob")
   Student s2 = new Student("Sue")
   t.addStudent(s1)
   t.addStudent(s2)

This looks a bit flat. 5 lines of code, 3 uses of the new keyword (which adds to the amount of text we have to read) and no sense of the hierarchical relationship between teachers and students. This is fine in such a simple case, but in a much more complicated case this code block would be horrible to read.

Wouldn't it be nicer to be able to do something like this?:

   teacher ("Jones") {
      student ("Bob"){}
      student ("Sue"){}
   }

This uses simple-to-read bracket notation, is much less typing and gives a sense of the hierarchy of the data.

This is what Groovy allows us to do with builders. We essentially have the capacity to create a domain specific language around the domain of "building our domain". If this sounds complicated, it isn't. It just means that we can use the minimum amount of syntax to specify the boiler plate tasks of creating object instances, whilst at the same time showing their parent / child relationships.

Groovy builders are an implementation of the GOF Builder pattern, but they are more than that. They make clever use of the powerful syntax and dynamism of the language to give you efficient creational code, cleaner than XML and much cleaner than just using vanilla Java code.

An Example

Another example of the recurring need to create structural data is XML. The Java DOM API lets us build XML documents, but the same problem applies, for example, to create the XML below:
   <?xml version="1.0"?>
   <teacher name = "Jones">
      <student name = "Bob">
      <student name = "Sue">
   </teacher>
We would need something like the following fragment:
   ..
   Element teacher = doc.createElement("teacher");
   teacher.setAttribute("name", "Jones");
   Element student1 = doc.createElement("student");
   Element student2 = doc.createElement("student");
   student1.setAttribute("name", "Bob");
   student2.setAttribute("name", "Sue");
   teacher.appendChild(student1);
   teacher.appendChild(student2);
   ..

Yech! Very noisy and very flat. We don't get a real sense of structure of the data from looking at the code, and we are wallowing in implementation details of the API, with method calls like setAttribute, appendChild and createElement.

Groovy supplies a MarkupBuilder() out of the box. Let's look at how we would generate the same XML as above using Groovy.

def builder = new groovy.xml.MarkupBuilder(writer)
   builder.teacher (name: "Jones") {
      student (name: "Bob") {}	
      student (name: "Sue") {}
   }

Definitely much cleaner and neater.

Looks too simple to work

It all looks a bit too simple to work, but in fact, if you look at my blog, Creating a Domain Specific Language with Groovy , you will see how you can use method interception and closure passing to create "pretend" methods and query their parameters, thus giving you the power to dynamically tailor behaviour. In this case, the methods "teacher" and "student" don't actually exist, but Groovy uses the calling mechanism to create the XML on the fly, as your code tries to call the methods. In the example above, it writes the resulting XML to the writer passed as a constructor parameter to the builder. This gives us an elegant way of constructing a structured XML document.

So what Else Can Builders Do?

I started this blog by saying that I would demonstrate how I could make simple Java3d scenes easier to render. Let me wrap this effort in a disclaimer from the start, by saying that the Java3d library is very complex, and I will demonstrate a simple builder that can create scenes using a subset of the API. The idea here is not so much to build a complete layer on top of Java3d, as it is to illustrate that with certain, complex API's, we can gain enormous power from creating thoughtful builders in Groovy. [As an aside, though, creating a library of builders around the Java3d API seems like a really exciting and fun project, so if anyone has knowledge of Java3d and is interested in a challenge, let me know!]

The Java3d 'Problem'?

To create a 'Simple Universe' scene in Java 3d, we need to build a fairly rich object scene graph. The Java3d library provides us with a rather nice "ColorCube" which is easy to work with and view, so the minimal code we need to create a simple universe, with a visible object in it, is this:
..
def universe = new SimpleUniverse()
def group = new BranchGroup()
group.addChild(new ColorCube(0.1))
universe.getViewingPlatform().setNominalViewingTransform()
universe.addBranchGraph(group) 
..

But this presents the same problem as the other examples. There is a hierarchy here, but we certainly can't see it in the code, and we are adding children and branch graphs to groups and universes, which looks very complicated. By default, we get this:

Which is a little boring so we should at least rotate it, so we can see it is a cube. This means doing the following:
   def cube = new ColorCube(0.3)
   def universe = new SimpleUniverse()
   def bGroup = new BranchGroup()
   def rot = new Transform3D()
   def trans = new Transform3D()
   rot.rotX 0.77 
   trans.mul rot
   rot.rotY 0.77
   trans.mul rot
   def tGroup = new TransformGroup(trans)
   bGroup.addChild(tGroup)
   tGroup.addChild(cube)
   universe.getViewingPlatform().setNominalViewingTransform()
   universe.addBranchGraph(bGroup)
This is not a Java3d tutorial, but you can see there is suddenly extra complexity here. It comes from the fact that to rotate something in a 3d scene, we have to insert a new object into the hierarchy, a TransformGroup. This transform group is given yet another object, a Transform, which can specify its rotation, scale and translation. It yields the slightly more interesting image:

A Solution

Let's look at the builder code that I can use to achieve the identical 3D scene hierarchy before I explain how I have implemented the builder:

   new Java3DBuilder().BranchGroup {
      TransformGroup (scale: 0.3, rotX:0.77, rotY:0.77) {
         ColorCube {}
      }
   }

That's it!! 14 lines down to 3! And look how intuitive it is by comparison. The BranchGroup clearly contains a Transform Group which has some parameters, and that in turn contains a ColourCube. Ok, this isn't magic, there is obviously some code needed to do this, but it is roughly 100 lines and it takes the form of extending an abstract class. The parsing logic is already there and so it really is easy to create a DSL which is intuitive to use, clean and simple. I also obtained a lot of leverage from the dynamicism of Groovy (features like dynamcic property setting, and constructor calling) and was able to implement some powerful default behaviour which covers a lot of the Java 3d classes that I haven't specifically considered.

Implementing a Builder

As I mentioned earlier, implementing a builder is just a question of extending an abstract Java / Groovy class. The full source is available here, and the full specification of groovy.util.BuilderSupport can be found in the Groovy documentation . The methods I was interested in are below:

public abstract class BuilderSupport extends GroovyObjectSupport {
   .. 
   protected abstract void setParent(Object parent, Object child);
   protected abstract Object createNode(Object name);
   protected abstract Object createNode(Object name, Object value);
   protected abstract Object createNode(Object name, Map attributes);
   protected abstract Object createNode(Object name, Map attributes, Object value);
   ..
} 

It should be fairly easy to imagine what happens here. Groovy will parse the builder code, calling the createNode method when it sees a new node name, and pass in the value and/or a map of attributes that it finds in the parameter list. In the example above, TransformGroup would be a new node and scale: 0.3, rotX:0.77, rotY:0.77 would be the attributes. When the close curly brace is reached, the setParent method is called with a reference to the parent and the child.

This means that when implementing these abstract methods we can decide on the creational behaviour, and build the hierarchy, which is what makes the model so powerful. Let's look at some of the highlights.

1. Dynamic Class Creation

Using a fairly standard Java classloading technique, I load the class specified in the builder. This means that the builder code need the user to specify the proper classname (e.g. "TransformGroup") but I didn't want to have to specify the full package name. To prevent this I chose the dynamic but slow and slightly repugnant option of appending some expected package names from the API and trying to load each class. This is not a good use of exceptions, and not my preferred option, but for now it keeps the builder code minimal. I have defined a list of packages in the code:
def packages = ['com.sun.j3d.utils.universe',
	'com.sun.j3d.utils.geometry',
	'javax.media.j3d',
	'javax.vecmath'] 

.. and provided the full package isn't specified (which I have allowed, to resolve any conflicting class names), I use the following to create the class:

   for (packageName in packages) {
      try {
         fullName = packageName + '.' + name
         cls = getClass().getClassLoader().loadClass(fullName)
      }
      catch (ClassNotFoundException e) {
         cls = null
      }
      if (cls)
         break
   }

2. Dynamic Property Setting

To obviate the need for huge switch statements, I can use Groovy's dymanic property setting to allow me to set attributes on the object. This means that as long as a valid property X exists (where there is a setX on the object), I can set it with the specified value. Since Groovy will throw an exception if the property doesn't exist, I can do a lot of the work with the code in the default case as below, and for the most part, don't need too much domain knowledge to implement that part of the builder:

def createNode(name, Map attributes) {
   def node
      switch (name.toLowerCase()) { 
         case 'transformgroup':
         node = buildTransformGroup(attributes)
         break
      default:
         node = createNode(name)
         attributes.each {key,val ->
            node.metaClass.setProperty(node,key,val)
         }
         break
      }
   node
}

Note the special case of the TransformGroup. More about that below.

3. Judicious Shortcutting, or Changing of Hierarchy

In general, we obviously want to use domain knowledge to ease our build effort. Here, since a TransformGroup always needs a Transform, I have combined them in my builder so that what looks like setting a property on a TransformGroup is actually setting it on the associated Transform. Under the covers this is creating two nodes with a parent child relationship, so what should be:

TransformGroup () {
   Transform3D (scale: 0.6, rotX:0.77, rotY:0.77) {
      ColorCube {}
}

Is actually more simply declared:


TransformGroup (scale: 0.6, rotX:0.77, rotY:0.77) {
      ColorCube {}
}

In the same vain, I have simplified the use of rotation of transforms. In Java3d, these need some vector calculation to combine them, but it is a distracting nuisance to create (and throw away) these vector classes during initialisation. In the special case of the TransformGroup, I used the powerful case statement in Groovy to simplify the use of rotx, roty or rotz. By default, though, I just delegate the setting of any TransformGroup property to its child Transform:
attributes.each {key,val ->
   switch (key.toLowerCase()) {
   case ['rotx', 'roty', 'rotz']:
      // invoke the method on the temp. transfrm and multiply together 
      temp.metaClass.invokeMethod(temp,key,val)
      transform.mul(temp)
      break
   default:
      transform.metaClass.setProperty(transform,key,val)
   break
   }
}

Experimenting With the Results

So, in about 100 lines of code, we have a builder which makes it very easy to play with the basic 3d universe. Because a lot of the build code is dynamic and generic, I can make use of many of the classes in java3d that I don't know anything about yet, so it really is a successful domain specific language. I can now experiment with the library using much simpler code.

For example, using a Cone is more complicated than the ColorCube because you need to specify a material for it. Also, putting two objects into our universe can be done in such a way that they can be rotated and moved independently of each other. So if I want to create a cube impaled on a cone, I can experiment with a small body of code that actually gives me a clue what it represents. Note that although this is a DSL, and looks somewhat declarative, it is in fact ordinary Groovy code, and so any valid Groovy statements can appear in between the node declarations.

..
builder.BranchGroup {
   TransformGroup (scale: 0.4, rotX:0.67, rotY:0.53) {
      ColorCube {}
   }
   TransformGroup (scale: 0.65, rotZ:0.22, translation: new Vector3f(0.1f,0.1f,0.1f)) {
      def appearance = new Appearance()
      appearance.setMaterial(new Material(green,green,green,green, 0.1f))
      Cone(appearance: appearance) {}
   }
}
..

Where to From Here?

There is a lot missing from this builder, but it wouldn't be very hard to add. As I said earlier, if you are interested, let me know. In general, there is nothing to stop you creating different builders to be used together, or nesting builders to create more complicated hierarchies. I hope I have convinced you that builders are a very important part of any API to provide, and failing that, a very good approach to creating a layer of abstraction that simplifies the use of existing API's.

References:

Download the code here.
GOF Builder Pattern
Groovy Builder Documentation

Talk Back!

Have an opinion? Readers have already posted 17 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Jeremy Meyer adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Jeremy has been designing and developing software for over 20 years, as well as teaching its mastery. He is fascinated by all aspects of architecture, design and development, the philosophical, the psychological and the aesthetic. He currently heads up the training division at hybris Software, a fast growing and very exciting eCommerce company.

This weblog entry is Copyright © 2010 Jeremy Meyer. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use