[Authored by Sawan Vaidya and Minesh Maharjan.]
Groovy is a succinct yet powerful programming language. We had our first brush with it a few years back when it was the new kid on the block. We liked it then, and decided that further investigation was in order before we consider using it for our own products. We decided to pick some moderately complex projects to test Groovy’s maturity and capabilities. The projects would need to go through the most common programming scenarios at Deerwalk and also be fun! We picked the classical Maze project mostly because we wanted to test Groovy’s savvy with Collections, which are central to Deerwalk’s needs.
Our Maze solving program consists of three classes, written in both Java and Groovy for comparison. The Coordinate class has properties and methods which allow us to position the player in the maze. The Solver class initializes the Maze class with the maze raster and the starting and ending coordinates. The Maze class has methods and properties which constitute the algorithm to solve the maze. The algorithm that we used to solve the maze is popularly known as the right hand rule. In defining our maze, we created a binary raster, in which '0's represented the path whereas the '1's represented the walls.
Before we continue, please note that our Maze solver solution below is intended to explore the functionality of Groovy and to get familiar with the language. We weren’t trying to write the best or even the most sensible Maze solver and this solution wouldn’t work well for large problemsets.
Even in the beginning stages of the project, Groovy proved to be much easier and cleaner to deal with. As opposed to Java, Lists and Maps are implicitly imported into the Groovy and are part of the language. With a square bracket syntax to initialize Lists, Groovy makes it look like Arrays and Lists are synonymous. Figure 1 and 2 shows the difference in initializing the same maze in Groovy and in Java.
Figure 1: Initializing maze in Java
Figure 2: Initializing maze in Groovy
Constructors in Groovy are smarter than their counterparts in Java. With the Groovy map constructor, a developer can completely avoid Overloading. The second subtle but important advantage offered by this feature is for the programmer to completely forget about the order (and type, described later) of the arguments needed to be supplied to the constructor. Groovy intelligently matches the map keys with the properties of the class. See Figure 4 for an example of the Groovy map constructor.
Figure 3: Java constructor call
Figure 4: Groovy constructor call
This advantage is much more apparent as the number of arguments to the constructor increase.
In Groovy, a method and/or object need not know about the type of the parameter until runtime. The def keyword can substitute any return type or variable type declaration. For instance, in our Groovy Maze class, instances of the Coordinate class are used extensively, without us having to explicitly state the type anywhere.
This yields to stricter data abstraction to the Maze class. The facade of the Maze class will require absolutely no change, should we need to make any change to the Coordinate class. Also, given another class has the same methods and properties, it can be used to completely replace the Coordinate class. The advantage of this type of “Duck typing” in speed of development and flexibility are well known. In certain cases, they even improve readability by reducing the verbosity of declarations.
However Dynamic Typing comes at a price of difficulty in debugging and sometimes reduced readability. By forgoing static typing, we lose the benefits of compile time checks and even some execution speed.
In order to solve the maze, four directions around any starting point (right, down, left and up) were checked in a recursive manner. If our Maze were large, we wouldn’t get away with using recursion. However, this is the most straightforward and intuitive solution for our purpose.
Figure 5 illustrates the solve method.
Figure 5: The solve method of the Maze class written in Groovy. This version doesn't utilize closures.
Since the lines (5-12) of the code snippet in Figure 5 have a similar structure, it appeared that they could be abstracted. Groovy's closure allowed us to do this in a remarkably succinct manner (See Figure 6). Compare it to the parallel Java code on the right (Figure 7). To simulate a closure, Java developers use Anonymous Inner Classes. However, Groovy has the real deal. Both closures and Anonymous Inner Classes allow us in essence to pass a method to another method called 'eachDirection'. Groovy did not require us to write an Interface, making it less rigorous, syntactically.
Figure 6: The solve method of the Maze class written in Groovy. This version utilizes a closure.
Figure 7: The solve method of the Maze class written in Java
Though we did not discuss here several other Groovy features such as dynamic Getters and Setters, optional return statements, optional return type declarations, operator overloading and interface implementation, Groovy proved to be the right choice for us. This conclusion was reached not only because of the terseness and readability of Groovy but also in the time taken to write the code. While Groovy cannot match Java’s execution speed, when optimizing for Developer’s time and maintainability, Groovy trumped Java. However, one of our favorite features of Groovy is that it doesn’t force us to choose between itself and Java. It wasn’t just one specific Groovy feature, but the collection of all the developer targeted improvements that made it a great choice for Deerwalk.
Deerwalk Inc. currently utilizes Groovy and Grails for developing most of its web products.
You can download the full code of the Maze project by clicking here.