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:

  1. Declare one structure (e.g. class, interface, enum) per file
  2. Name the file after the structure declared in the file. For example, if you declare class Person{...} then name the file Person.groovy
  3. 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:

First attempt at Person.groovy - will fail
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:

Second attempt at Person.groovy - will work
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:

Main.groovy
package mypackage.app

import mypackage.Person

def jim = new Person(name: 'Jim')
println jim.name

Note that I’m actually creating two packages:

  • mypackage will contain the Person class
  • mypackage.app will contain the Main class

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:

  1. A mypackage subdirectory has been created and contains Person.class
  2. A mypackage/app subdirectory has been created and contains Main.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:

  1. Download the groovy-all jar into the directory containing the mypackage subdirectory.
  2. Now run java -cp groovy-all-2.4.4.jar:. mypackage.app.Main
  1. Short form: groovyc *.groovy