87. Organising your code
When you start writing larger code bases you’ll need to think about how to organise your code. You don’t have to do this if you’re using Groovy to write scripts that can fit into a reasonable file size (say, 400 lines) but really large files can become difficult to scroll through and debug. There are two complementary tactics you can use when working with larger bodies of code:
- Break the code into packages
- Put the code files into a directory structure that reflects your package structure
We’ll start by looking at packages and then move onto directory structures.
Packages
Code that doesn’t specify a package is said to be in the default package. Most of the code we’ve looked at so far has been lacking a specific package and is automatically put into the default package - this is perfectly fine for small applications and scripts. However, once you start to build larger codebase you’ll want to start breaking your classes, enums, interfaces etc into separate files and collect them into packages.
The generally agreed approach to structuring larger codebases is to:
- Declare one structure (e.g. class, interface, enum) per file
- Name the file after the structure declared in the file. For example, if you declare
class Person{...}then name the filePerson.groovy - Place these files into one or more packages
Following this approach will help you avoid a mess of files that makes it hard to debug problems later.
Let’s take a look at a basic example in which I have a script that declares a Person class and then tries to do something
with it:
package mypackage
class Person {
def name = ''
}
def jim = new Person()
println jim.name
If I put the above script into a file named Person.groovy and try to run it with groovy Person.groovy
it will give me the following error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Person.groovy: 3: Invalid duplicate class definition of class mypackage.Person :\
The source Person.groovy contains at least two definitions of the class mypacka\
ge.Person.
One of the classes is an explicit generated class using the class statement, the\
other is a class generated from the script body based on the file name. Solutio\
ns are to change the file name or to change the class name.
@ line 3, column 1.
class Person {
^
1 error
Essentially, this is caused by Groovy trying to create a Person class around the def jim = new Person() - Groovy
assumes that you want to create a class with the same name as the file but you have already declared a Person class in the file.
If we’re following the rule of “One structure per file”, we should only declare the Person class in Person.groovy:
package mypackage
class Person {
def name = ''
}
In the same directory we’ll then create Main.groovy and put it into the mypackage.app package as follows:
package mypackage.app
import mypackage.Person
def jim = new Person(name: 'Jim')
println jim.name
Note that I’m actually creating two packages:
-
mypackagewill contain thePersonclass -
mypackage.appwill contain theMainclass
As Person isn’t in the same package as Main, we need to import it using the fully qualified name for the Person
class: mypackage.Person.
Compiling and running
Now that you have a Person.groovy file and a Main.groovy file you can run
groovyc Main.groovy Person.groovy
to compile the two files1. Once groovyc has completed you’ll notice the following:
- A
mypackagesubdirectory has been created and containsPerson.class - A
mypackage/appsubdirectory has been created and containsMain.class
If I now run groovy Main I’ll get Jim’s name displayed as I expect but be warned that, whilst this call to groovy is
using the compiled Person class, it is actually using the Main.groovy file and not the compiled version. In most cases
you would keep Main.groovy as a script and not worry about putting it into a package - this is just for demonstration
purposes.
You can run your compiled Groovy classes using Java by doing the following:
- Download the groovy-all jar into the directory containing the
mypackagesubdirectory. - Now run
java -cp groovy-all-2.4.4.jar:. mypackage.app.Main
- Short form:
groovyc *.groovy↩