Contracts for Groovy

Tutorial – GContracts: A Design by Contract extension for Groovy

AndreSteingress

Andre Steingress introduces us to GContracts – an extension library for Groovy

Introducing GContracts

Bertrand Meyer, the creator of the Eiffel programming language, introduced the concept of Design by Contract, a technique for improving software robustness and reliability by specifying formal contracts between class implementations.

In a way it is surprising that DbC never found its way into the programming language main stream. At times it has reportedly been under the top 25 requested issues of Sun’s issue tracker for the Java programming language.

A contract is an agreement between a class and its clients (classes using the class) and is a first-level citizen of the programming language, standing on the same level as program instructions and expressions.

To see how contracts can help improve software reliability and robustness, we will start with the Rocket class implementation (see Listing 1). Before we jump right into specifying contracts, let’s have a look at a technique that seems similar to DbC at first sight: assertive programming. 

Listing 1

 

class Rocket  {
  int speed
  private boolean started
  
  def start() { started = true }
  boolean isStarted() { started }
  def accelerate()  { speed = speed + 5 }
  def brake() { speed = speed - 5 }
}

 

Assertive Programming

Programming is all about formalizing internal thought models. Even with the finest requirement documents and project methodologies, in the end internal thought models drive the actual doing.

But with internal models come implicit assumptions about the behaviour of certain things or software elements. Assertive programming is about adding assertions to code places where implicit assumptions need to be externalized and proven.

Let’s have a look back at the Rocket class. Once instantiated, a rocket object can be started, accelerated and braked. The class has an internal state, resembling its current speed, and a flag indicating whether the rocket has been started or not. As you will see during the course of this article, DbC especially fits domain classes in a sense of the domain-driven design (DDD) approach, rather than record classes.

The Rocket class author implicitly assumed that Rocket#brake() would never be called before Rocket#accelerate(). But as implicit assumptions should be embedded into the source code, we need to state this assumption. In order to do so, one option is to use Groovy’s power assertion statement: assert.

Listing 2

 

class Rocket  {
  int speed
  private boolean started

  def start() { started = true }
  boolean isStarted() { started }

  def accelerate()  { 
    assert started
    speed = speed + 5
  }

  def brake() { 
    assert started
    speed = speed - 5 
  }
}

 

If Rocket#brake() is now called before Rocket#accelerate(), an AssertionError is thrown and the following message is shown:

 

Assertion failed: 
assert started
       |
       false

 

As can be seen from the code sample, the assertion uses a Boolean expression to state its assumption that a rocket must always be started prior to brake it. In Rocket#brake() and Rocket#accelerate() we can see the first contract element: the precondition.

A precondition defines what must be true in order to let the method succeed. In our case, the rocket instance needs to be started, before Rocket#brake() or Rocket#accelerate() succeeds. It is the Rocket client’s responsibility to start the rocket instance. The stronger the precondition, the more work needs to be done by the client. This precondition is the first part of our Rocket class contract (as a side-note: a contract itself has no representing source code element, it’s simply an overlapping term for all conditions a class defines in terms of its DbC elements, one of them being preconditions). We will refer to a precondition as a contract element.

From assertions to contracts

As can be seen in Listing 2, adding assertions quickly bloats code and makes the actual business logic less readable. There is another not so obvious catch: assertions are not compliant with two important object-oriented principles, inheritance and polymorphism.

Just imagine if someone decided to create a subclass of Rocket overriding the Rocket#accelerate() method by increasing only by 1. As a result, the implementation of Rocket#brake() would break as speed becomes smaller than zero, being an invalid state for rocket objects (see Listing 3). With assertions, there is nothing a class author can do against overriding preconditions in subclasses.

Listing 3

class AnotherRocket extends Rocket {
    def accelerate()  { 
        assert started
        speed = speed + 1
    }
}
    
def rockets = [new AnotherRocket(), new Rocket()]
def result = rockets.collect { Rocket rocket ->
    rocket.start()
    rocket.accelerate()
    rocket.brake()
    rocket.speed
}

println result // output: [-4, 0]

 

Another disadvantage is that a subclass could easily erase the contract element defined by its parent class via prohibiting the assertion statement leading to maybe unexpected consequences.

It too violates the Liskov Substitution Principle, as it would result in subclass instances that cannot be replaced with parent class instances and vice versa, leading to corrupt and unpredictable runtime behaviour. As can be seen in the last code example, the Rocket instance behaves correctly when methods are called in the collect closure, whereas the instance of type AnotherRocket gets into an invalid object state. Just imagine the code in the collect closure would be hidden under various layers and indirections – without actually knowing the concrete type, a client could never deduce an inappropriate object state just by viewing the source code.

As we will see in the next section on GContracts, there are even more advantages by applying DbC instead of working with plain assertions:

  • Contract inheritance
  • Accessing old instance variable values in Boolean expressions
  • Getting the method’s return value in Boolean expressions
  • Adding class contracts to the generated documentation
  • Package- or class-level configuration of assertion enabled code

We can’t go into each of the enlisted points in the rest of this article, just remember that DbC is more than simply putting assertions into source code – it is fully integrated in the object-oriented programming paradigm.

Design by Contract for Groovy

GContracts is an extension library for the Groovy programming language. It supports Groovy versions starting from 1.8.0, comes without any external dependencies and is released under a BSD-styled license.

To include GContracts in a Maven or Ivy based environment, you would simply include:

‘org.gcontracts:gcontracts-core:1.2.5′

 

Once GContracts jars are available in the classpath, you just need to import the org.gcontracts.annotations.* package to define class contracts. As soon as GContracts is detected by the Groovy compiler, contracts will be automatically added to the generated bytecode during the compilation process.

Contract Annotations

The package org.gcontracts.annotations.* contains three annotations to be used for specifying contract elements on Groovy classes:

  • @Requires for method preconditions
  • @Ensures for method postconditions
  • @Invariant for class invariants

Each annotation will use a language construct that has been introduced in Groovy 1.8: closure annotation parameters. Groovy supports closures as annotation parameters by keeping Java byte-code compatibility. As closures are compiled to inner classes, this is solved by adding a java.lang.Class parameter to the annotation, so don’t get confused when, looking at @Requires, @Ensures or @Invariant annotation implementations, you can’t find a Closure attribute – it will be resolved to java.lang.Class by the Groovy compiler.

To see how closure annotation parameters look like, take a look at Listing 4. It shows the imaginary annotation @Since which could be used to execute code only within a specific version range. The actual code checking the version number is specified within a closure annotation parameter.

Listing 4

 

@Since( { version >= 1.0 } )

 

In this case, the closure annotation parameter is the only annotation parameter. GContracts annotations make heavy use of closure annotation parameters to define contracts. So let’s start by exploring GContracts contract annotations, starting with the precondition annotation @Requires.

  

Contract Element Part 1: Preconditions with @Requires

@Requires is supposed to be used for precondition specifications. It can only be applied on instance method, static method and interface method declarations. Listing 5 shows how our Rocket class looks like after manually transforming all assertion statements to preconditions.

 

import org.gcontracts.annotations.*

class Rocket  {

    int speed
    private boolean started
    
    @Requires()
    def start() { started = true }
    
    boolean isStarted() { started }
    
    @Requires({ started })
    def accelerate()  {
        speed = speed + 5
    }
    
    @Requires({ started })
    def brake() { 
        speed = speed - 5 
    }
}

 

As can be seen with respect to the refactored Rocket class, the introduction of @Requires for preconditions leads to much cleaner code, as all assertion code is moved to closure annotation parameters.

Code inside the closure annotation can access method parameters, publicly accessible instance variables and methods. If accelerate would come with a parameter dx indicating the speed delta, @Requires could be used to check for its validity, too.

Listing 6

 

@Requires({ started && dx > 0 })
def accelerate(int dx)  {
    speed = speed + dx
}

 

The contract author is free to use arbitrary Groovy language construct within the precondition, as long as the resulting code is a Boolean expression. Otherwise, GContracts will detect non-boolean expressions and will throw a compile-time error.

As already noted, another major benefit of using GContracts is its integration with inheritance and polymorphism. If we would enhance the AnotherRocket subclass by overwriting Rocket#accelerate() with a custom implementation, we wouldn’t need to define the precondition again. It would simply be inherited as AnotherRocket is a Rocket descendant.

Listing 7

 

class AnotherRocket extends Rocket {
    def accelerate()  {  // @Requires will be inherited from Rocket
        speed = speed + 1
    }
}
    
def rocket = new AnotherRocket()
rocket.accelerate()

 

A call to AnotherRocket#accelerate() without a prior call to AnotherRocket#start() would fail with a slightly modified Groovy power assertion message:

 

org.gcontracts.PreconditionViolation: <org.gcontracts.annotations.Requires> Rocket.java.lang.Object accelerate()

 

started

|

false

 

 

The power assertion message shows the part of the contract that has been violated, shows the results of sub-expressions and points to the part of the Boolean expression being “false”. This works even better with complex Boolean expressions:

 

org.gcontracts.PreconditionViolation: <org.gcontracts.annotations.Requires> Rocket.java.lang.Object accelerate()

 

((1 + 1) * 3) == 7

    |    |    |

    2    6   false

 

 

 

In addition, GContracts supports redefining base class preconditions. A precondition redefinition can be achieved by providing an additional @Requires annotation to AnotherRocket#accelerate() (see Listing 8).

Listing 8

 

class AnotherRocket extends Rocket {

    @Requires({ !started })
    def accelerate()  { 
        speed = speed + 1
    }
}

 

During compilation, GContracts will join both preconditions to a single precondition. In order to still support inheritance and polymorphism, preconditions can only be weakened by subclasses, thus the precondition above would be equal to @Requires({ started || !started }) – an assertion always being “true”.

Contract Element Part 2: Postconditions with @Ensures

So far, we did not talk about the general concept of postconditions. As noted in the previous sections, preconditions are a way to set certain boundaries for clients using the class – postconditions invert that principle. A postcondition states what in turn is guaranteed by the class author as long as the precondition is satisfied.

GContracts provides @Ensures for defining postconditions. The annotation can be applied on instance method, static method and interface method declarations. Back to the Rocket class example. Postconditions could be added to Rocket#brake() and Rocket#accelerate(), to state that both methods change the rockets speed in either way.

Listing 9

 

import org.gcontracts.annotations.*

class Rocket  {
    int speed
    private boolean started
    
    @Requires()
    def start() { started = true }
    
    boolean isStarted() { started }
    
    @Requires({ started })
    @Ensures({ speed - old.speed == 5 })
    def accelerate()  {
        speed = speed + 5
    }
    
    @Requires({ started })
    @Ensures({ old.speed - speed == 5 })
    def brake() { 
        speed = speed - 5 
    }
}

 

 

The postcondition code in Listing 9 shows another DbC-specific feature available solely for postconditions: the old variable.

Inside @Ensures, a special variable named old is available. It holds the values of all typed instance variables of the current object. In our case, it holds the speed instance variable, which is used to exactly determine whether the speed has changed by 5.

There is a second variable available in closure annotation code being used to refer to the actual method’s result – assuming the method’s return type is not void – it is called result. If accelerate would return the current speed, we could refer the return value in @Ensures (see Listing 10).

Listing 10

 

@Requires({ started })
@Ensures({ result - old.speed == 5 })
def accelerate()  {
  speed = speed + 5
  return speed

 

As it is the case with @Requires, all postconditions are populated down the inheritance hierarchy and multiple postconditions on a single method will automatically be merged during the compilation process. In contrast to preconditions, the merging rule for postconditions is such that postconditions down the class hierarchy can only strengthen the base class postcondition, never weaken it.

Contract Element Part 3: Class Invariants with @Invariant

So far for our Rocket class … but wait. What happens if we call Rocket#brake() multiple times without ever calling Rocket#accelerate()? Right, the speed would turn smaller than zero. But as it turns out, speed must never be smaller than zero, being a formative property of Rocket objects.

A property that has to be fulfilled throughout the entire object life-time is called the class invariant. Class invariants can be specified with @Invariant. Again, the annotation uses a single closure annotation parameter and again the contract element is inherited to all direct and indirect subclasses.

Listing 11

 

@Invariant({ speed >= 0 && speed <= Integer.MAX_VALUE - 5 })
class Rocket  { ... } 

def rocket = new Rocket()
rocket.start()
rocket.accelerate()
rocket.brake()
rocket.brake() // fails with ClassInvariantViolation messsage

 

As you might have noticed, the speed instance variable isn’t a real instance variable, it is a plain-old Groovy object property. GContracts automatically adds class invariant checks for all property accessor methods, so calling rocket.speed = -1 would again result in a class invariant violation.

Contracts, contracts, everywhere …

GContracts becomes truly powerful when using its annotations in library or framework base classes. As it supports contract annotations on Groovy interfaces and abstract classes, this is can be done straight-forward.

Let us introduce a RocketAPI embedding the general contract directly in the interface definition.

Listing 12

 

import org.gcontracts.annotations.*

interface RocketAPI {
  def start()
  boolean isStarted()
  int getSpeed()
    
  @Requires({ started })
  @Ensures({ result - old.speed == 5 })
  def accelerate()
    
  @Requires({ started })
  @Ensures({ old.speed - speed == 5 })
  def brake()  
}

@Invariant({ speed >= 0 && speed <= Integer.MAX_VALUE - 5 })
class Rocket implements RocketAPI  {

    int speed
    private boolean started
    
    def start() { started = true }
    boolean isStarted() { started }
    
    def accelerate()  {
        speed = speed + 5
        return speed
    }
    
    def brake() { 
        speed = speed - 5 
    }
}

def rocket = new Rocket()
rocket.accelerate()

 

As a result, all classes implementing the RocketAPI interface must adhere to the specified contract. As said earlier, preconditions can only be weakened, postconditions can only be strengthened by implementation classes.

A call to Rocket#accelerate() without a preceding call to Rocket#start() would now fail with the following violation message:

 

org.gcontracts.PreconditionViolation: <org.gcontracts.annotations.Requires> RocketAPI.java.lang.Object accelerate() 

started
|
false

 

Notice that the error message’s originator has changed to RocketAPI#accelerate(). As the contract elements will be embedded in the resulting bytecode, we could put the RocketAPI into a jar and every project including the jar and GContracts would automatically be forced to comply with the specified contract.

Conclusion

This article only showed a selection of the most important core features of GContracts, with focus on the core contract annotations: @Requires, @Ensures and @Invariant.

More information on advanced features like the custom Groovydoc Ant task for embedding contracts into GroovyDoc generated class documentation, custom micro contract annotations for reusing small but reoccurring contract elements and disabling contracts on package- and class-level can be found at GContracts project wiki.

To start playing with contracts, download the most recent version (as of March 2012, 1.2.5) available on the project homepage or Maven central repository, add it to the applications classpath, import org.gcontracts.annotations – and your favourite programming language will magically support Design by Contract.

Design by Contract™ is a registered trademark of Interactive Software Engineering Inc.

 

Author Bio: 

Andre Steingress works as Groovy & Grails developer in Linz, Austria. He regularly blogs at http://blog.andresteingress.com, holds workshops and trainings focusing on Groovy, Grails and Spring, enjoys contributing to the Groovy programming language and is the creator of GContracts, a Design by Contract library for Groovy.

This article originally appeared in Java Tech Journal – The Groovy Universe. Check out the rest of that issue here

 

Author
Comments
comments powered by Disqus