How to Implement a Groovy Builder
Now that you have seen the language features that allow the Groovy builder syntax to work, you can have a go at implementing one to meet the original example. Thankfully, it is not necessary to deal with method interception and scope delegation to implement your own builder. Groovy handles all that complicated logic for you in the BuilderSupport class, which you must extend to implement a builder. It has the following abstract methods:
protected void setParent(Object parent, Object child);
protected Object createNode(Object name);
protected Object createNode(Object name, Object value);
protected Object createNode(Object name, Map attributes);
protected Object createNode(Object name, Map attributes, Object value);
When your builder code is executed, BuilderSupport implements an algorithm that:
- Calls the relevant
createNode method for each node that is executed, which allows you to return an object representing the current node.
- Calls the
setParent method; the current parent is passed as well as the node you have just created. The setParent method is not called for the root node, as no parent exists in this case.
- Delegates execution to the attached closure, this allows you to use the nested closure syntax.
- Calls the
nodeComplete method. This is not an abstract method so does not need to be implemented, but is often overridden.
Using some examples from the code above will demonstrate how BuilderSupport decides which createNode method to call.
From the example, the screen, navigation, content, and table nodes would cause the following to be executed because they have no arguments, except from the closure to be used to construct child nodes:
createNode(Object name)
The title and footer nodes perform a single method call with one argument and so cause the following method to be executed:
createNode(Object name, Object value)
The navItem nodes use the named argument approach causing the following method to be executed:
createNode(Object name, Map attributes)
Now, what if the syntax of the navItem node was changed slightly?
navItem( visible: true, focus: true, "Home")
In this case, because both named arguments and a single value are passed, the final createNode signature would be executed:
createNode(Object name, Map attributes, Object value)
Groovy builders are often best implemented as a syntactic sugar wrapper over an existing framework of Java code. Following this approach, you will implement your Groovy builder on top of the existing Java builders that are required for the very first Java builder examples. This means that, under the scenes, the following Java builder classes already exist. Take a look in the downloadable source code to see these classes.
- ScreenBuilder
- NavigationBuilder
- NavItemBuilder
- ContentBuilder
The following also needed to be introduced to handle the remaining cases:
- TableBuilder
- PassthroughBuilder
In addition, to make the Groovy builder as simple as possible, each of these Java builders implements a GroovySupportingBuilder interface:
public interface GroovySupportingBuilder extends Builder {
GroovySupportingBuilder withData( Object value );
GroovySupportingBuilder withAttributes( Map attributes );
GroovySupportingBuilder withBuilder( Builder builder );
}
The aim of this interface is to be able to delegate off the specific implementation of how to handle the arguments passed to the builder, while leaving the builder code very clean and able to handle the syntactic logic. The withData method will handle the Object value arguments that are sent to the builder, while the withAttributes method handles the named arguments that are passed to the Groovy builder and converted into a Map by Groovy. By using the existing Java builders, you can implement a Groovy builder using the code from Listing 3.
The first thing you should notice is that the code to implement the Groovy builder is Java, not Groovy. It could just as easily be implemented in Groovyin fact it has been under the GScreenBuilderOld Groovy class in the code download. However, execution is more efficient if the builder is implemented in Java as there is still quite a big difference between execution speed between classes compiled from Groovy and straight Java classes.
In the constructor of the builder, a lookup map of Java builder prototypes is instantiated so the correct Java builder can be determined based on the node name received by the builder. The setParent method has been implemented to do nothing; instead, the building of the object graph is handled in the nodeComplete method. The strategy is that a Java builder is selected for each node as the hierarchy of nodes is descended and the resulting object is constructed and added to the object graph on the way back up.
You can see from each of the createNode methods that a Java builder is being looked up from the prototype map and the data supplied for the node is passed off to the relevant Java builder to handle in a context that understands how the data is to be used. The implementation of each of the Java builders is available in the downloadable source code.
The nodeCompleted method is responsible for passing the constructed Java builder into its parent so the object that is managed by the builder can be extracted and loaded into the parent. Once the hierarchy has finished executing, the build method must be called to allow the builder to extract the Screen object from the prepared ScreenBuilder.
Happy Building
Through Groovy builders, it is easy to create your own limited language for constructing complex object hierarchies. Having these Domain Specific Languages allows you to manage large object graphs with simpler code that is easier to understand. Using a layered approach when creating your own Groovy builder makes life much easier. Take the time to build the rules into less glamorous Java builders before creating the Groovy builder that will provide the final layer of syntactic sugar over the top. There are a number of Groovy builders that come supplied with Groovy. The most popular are probably:
Jon Dickinson is the principal consultant at Accolade Consulting. He focuses on helping organizations improve software delivery through agile development techniques.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.