94. The Shapes demo - Interfaces

One interface is defined within the shapes library: TwoDimensionalShape:

The TwoDimensionalShape interface
package org.groovy_tutorial.shapes

/**
 * An interface for basic two-dimensional objects
 *
 * @see <a href="https://en.wikipedia.org/wiki/List_of_two-dimensional_geometric\
_shapes">
 *     Wikipedia: List of two-dimensional geometric shapes</a>
 *
 * @author Duncan Dickinson
 */
interface TwoDimensionalShape {

    /**
     * The length of the path surrounding a 2D shape
     * @see <a href="https://en.wikipedia.org/wiki/Perimeter">Wikipedia: Perimet\
er</a>
     * @return the perimeter of the shape
     */
    BigDecimal getPerimeter()

    /**
     * The extent of a 2D shape in a plane
     * @see <a href="https://en.wikipedia.org/wiki/Area">Wikipedia: Area</a>
     * @return the area of the shape
     */
    BigDecimal getArea()

    /**
     * A handy display string
     * @return a text representation of the shape
     */
    String getDisplayInfo()

    /**
     * @return the name of the shape
     */
    String getShapeName()
}

The interface is declared using the interface keyword followed by the name: interface TwoDimensionalShape. Within the interface is the following method signatures:

  • BigDecimal getPerimeter(): will return the shape’s perimeter
  • BigDecimal getArea(): will return the shape’s area
  • String getDisplayInfo(): is used to prepare a handy description of the shape
  • String getShapeName(): returns the name of the shape (e.g. square or circle)

Each method signature is listed without a definition block ({...}) and it is up to the implementing class(es) to provide the “body” of the definition. The Circle class does just that:

The Circle class
package org.groovy_tutorial.shapes

import static java.lang.Math.PI

import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

/**
 * Describes a circle
 * @author Duncan Dickinson
 */
@EqualsAndHashCode(includes = 'radius')
@ToString(includeNames = true, includeFields = true, includePackage = true)
final class Circle implements TwoDimensionalShape {
    private static final String SHAPE_NAME = 'Circle'

    /** The radius of the circle */
    final BigDecimal radius

    /** The circle's perimeter (circumference) */
    final BigDecimal perimeter

    /** The circle's area */
    final BigDecimal area

    /**
     *
     * @param radius the radius of the circle (must be a positive number)
     * @throws IllegalArgumentException if radius <= 0
     */
    Circle(BigDecimal radius) throws IllegalArgumentException {
        ShapeUtil.checkSidesException(radius)
        this.radius = radius
        this.perimeter = calculatePerimeter(radius)
        this.area = calculateArea(radius)
    }

    /**
     * Helper function - defers to calculatePerimeter
     * @see #calculatePerimeter(Number)
     * @param radius
     * @return the circumference (perimeter)
     * @throws IllegalArgumentException if radius <= 0
     */
    static BigDecimal calculateCircumference(Number radius) throws IllegalArgume\
ntException {
        calculatePerimeter(radius)
    }

    /**
     * Calculates the perimeter of a circle using the formula: p = 2*Pi*r
     * @param radius
     * @return the perimeter
     * @throws IllegalArgumentException if radius <= 0
     */
    static BigDecimal calculatePerimeter(Number radius) throws IllegalArgumentEx\
ception {
        ShapeUtil.checkSidesException(radius)
        (2 * PI * radius) as BigDecimal
    }

    /**
     * Calculates the area of a circle using the formula: a = Pi*r^2
     * @param radius
     * @return the area
     * @throws IllegalArgumentException if radius <= 0
     */
    static BigDecimal calculateArea(Number radius) throws IllegalArgumentExcepti\
on {
        ShapeUtil.checkSidesException(radius)
        (PI * radius**2) as BigDecimal
    }

    /**
     * Calculates the circle's diameter using the formula: d = 2r
     * @param radius
     * @return the diameter
     * @throws IllegalArgumentException if radius <= 0
     */
    static BigDecimal calculateDiameter(Number radius) throws IllegalArgumentExc\
eption {
        ShapeUtil.checkSidesException(radius)
        (radius * 2) as BigDecimal
    }

    @Override
    String getDisplayInfo() {
        "$SHAPE_NAME: radius = $radius; diameter = $diameter; \
circumference = ${circumference}; area = ${area}"
    }

    /**
     * Just a convenience - equivalent to getPerimeter
     * @return the circumference
     */
    BigDecimal getCircumference() {
        perimeter
    }

    /**
     * A pseudo getter
     * @return the diameter
     */
    Number getDiameter() {
        calculateDiameter(this.radius)
    }

    @Override
    String getShapeName() {
        SHAPE_NAME
    }
}

You’ll notice that the Circle class doesn’t explicitly provide an implementation for getPerimeter() and getArea() as Groovy will generate these for the member fields.