Skip to content

Triangle Groovy Koan

Thought I’d post a “Groovy Koan.”

The following is a Groovy version of a solution to the Triangle Ruby Koan, which (in part) challenges the student to code the following problem:

# Triangle analyzes the lengths of the sides of a triangle
# (represented by a, b and c) and returns the type of triangle.
#
# It returns:
#   :equilateral  if all sides are equal
#   :isosceles    if exactly 2 sides are equal
#   :scalene      if no sides are equal
#
def triangle(a, b, c)
  # WRITE THIS CODE
end

What you’ll see here arguably represents a ‘sexier’ solution than many others out there.

It’s not all my own work: its an amalgam of the various versions that were discussed during the workshop that I talked about earlier. So thanks to my fellow participants.

Here goes nothin’…

First, the solution:

// file: triangle/Triangle.groovy
package triangle

class Triangle {
    static triangle(_a, _b, _c) {
        def coll = [_a, _b, _c]
        def (a, b, c) = coll.sort()
        if ((a + b) <= c)
            throw new TriangleException()
        TriangleType.values()[coll.unique().size() - 1]
    }
}

// NB: order is important here
enum TriangleType {
    EQUILATERAL, ISOSCELES, SCALENE
}

class TriangleException extends Exception {}

Rather than follow the Ruby Koan testing path I have decided to find my own way and use the very neat Spock testing framework. Viz:

// file: TriangleSpec.groovy
import spock.lang.*
import triangle.*

public class TriangleSpec extends spock.lang.Specification {

    @Unroll("#iterationCount: <#a, #b, #c> represents an EQUILATERAL triangle.")
    def "test equilateral triangles have equal sides"(a, b, c) {
        expect:
        TriangleType.EQUILATERAL == Triangle.triangle(a, b, c)
        where:
         a |  b |  c
         2 |  2 |  2
        10 | 10 | 10
    }

    @Unroll("#iterationCount: <#a, #b, #c> represents an ISOSCELES triangle.")
    def "test isosceles triangles have exactly two sides equal"(a, b, c) {
        expect:
        TriangleType.ISOSCELES == Triangle.triangle(a, b, c)
        where:
         a |  b |  c
         3 |  4 |  4
         4 |  3 |  4
         4 |  4 |  3
        10 | 10 |  2
    }

    @Unroll("#iterationCount: <#a, #b, #c> represents a SCALENE triangle.")
    def "test scalene triangles have no equal sides"(a, b, c) {
        expect:
        TriangleType.SCALENE == Triangle.triangle(a, b, c)
        where:
         a |  b |  c
         3 |  4 |  5
        10 | 11 | 12
         5 |  4 |  2
    }

    @Unroll("#iterationCount: <#a, #b, #c> represents an illegal triangle.")
    def "test illegal triangles throw exceptions"(a, b, c) {
        when:
        Triangle.triangle(a, b, c)
        then:
        thrown(TriangleException)
        where:
        a | b |  c
        0 | 0 |  0
        3 | 4 | -5
        1 | 1 |  3
        2 | 4 |  2
    }
}

Does it work? Yes. Yes it does:

And in (a somewhat round-about) homage to Jim Wierich (author of Ruby’s equivalent ‘rake‘ tool), I have used Gradle for build automation and dependency management:

// file: build.gradle
apply plugin: "groovy"

repositories {
    mavenCentral()
}

dependencies {
    groovy("org.codehaus.groovy:groovy-all:2.0.5")
    testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}

// ALWAYS do tests regardless of up-to-date checking
test.outputs.upToDateWhen { false }

May the above help you along your journey on your path to enlightenment!

Tags: , , ,

C, Java Enterprise Edition, JEE, J2EE, JBoss, Application Server, Glassfish, JavaServer Pages, JSP, Tag Libraries, Servlets, Enterprise Java Beans, EJB, Java Messaging Service JMS, BEA Weblogic, JBoss, Application Servers, Spring Framework, Groovy, Grails, Griffon, GPars, GAnt, Spock, Gradle, Seam, Open Source, Service Oriented Architectures, SOA, Java 2 Standard Edition, J2SE, Eclipse, Intellij, Oracle Service Bus, OSB