On Github thecodesmith / groovy-dsl-talk
Master programmers think of systems as stories to be told rather than programs to be written.
-- Robert C. Martin: Clean Code
Write code in the language of the problem domain
The TRUE Java scripting language
Groovy's flexible & malleable syntax
Java:
public class Action {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Groovy:
println "Hello"
Java
Rate<LoanType, Duration, BigDecimal>[] table() { ... }
Groovy
def table() { ... }
Lists
def days = [Monday, Tuesday, Wednesday]
Maps
def states = [WI: 'Wisconsin', TX: 'Texas']
Ranges
def weekdays = Monday..Friday def allowedAge = 18..65
Java
move(left);
Groovy
move left
Example: Simple interpolation equation
Java
BigDecimal uMinusV = c.subtract(a);
BigDecimal vMinusL = b.subtract(c);
BigDecimal uMinusL = a.subtract(b);
return e.multiply(uMinusV)
.add(d.multiply(vMinusL))
.divide(uMinusL, 10, BigDecimal.ROUND_HALF_UP);
Groovy
(d * (b - c) + e * (c - a)) / (a - b)
When closures are last parameter, they can be outside parentheses
Example: Custom control structures
// method taking a closure:
def unless(Boolean condition, Closure action) {
log.info "Executing action"
action()
}
// usage:
unless (account.balance < 100.euros) {
account.debit 100.euros
}
a + b // a.plus(b) a - b // a.minus(b) a * b // a.multiply(b) a / b // a.divide(b) a % b // a.modulo(b) a ** b // a.power(b) a & b // a.and(b) a ^ b // a.xor(b) a[b] // a.getAt(b) a << b // a.leftShift(b) a >> b // a.rightShift(b) +a // a.positive() -a // a.negative() ~a // a.bitwiseNegate()
Currency
15.euros + 10.dollars
Distances
10.miles * 3 == 30.miles
Workflow and Concurrency
taskA | taskB & taskC
Credit an account
account << 10.dollars account += 10.dollars account.credit 10.dollars
Methods of the form getXyz() can be accessed like properties
class Person {
String firstName
String lastName
String getFullName() {
return "$firstName $lastName"
}
}
person = new Person(firstName: 'Brian', lastName: 'Stewart')
// Accessed like a property, but calls getFullName() under the hood:
println person.fullName
Other builders: XMLBuilder, JSONBuilder, SwingBuilder, etc.
def mkp = new MarkupBuilder()
mkp.html {
head {
title "Groovy in Action"
}
body {
div(width: "100") {
p(class: "para") {
span "Table of Contents"
}
}
}
}
count = 0
new SwingBuilder().edt {
frame(title: 'Counter', size: [300, 300], show: true) {
borderLayout()
textlabel = label(text: 'Click the button!', constraints: BL.NORTH)
button(
text: 'Click Me',
constraints: BL.SOUTH,
actionPerformed: {
count++
textlabel.text = "Clicked ${count} time(s)."
}
)
}
}
// equivalent to: turn(left).then(right)
turn left then right
// equivalent to: take(2.pills).of(chloroquinine).after(6.hours)
take 2.pills of chloroquinine after 6.hours
// equivalent to: paint(wall).with(red, green).and(yellow)
paint wall with red, green and yellow
// with named parameters too
// equivalent to: check(that: car).is(stopped)
check that: car is stopped
// with closures as parameters
// equivalent to: given({}).when({}).then({})
given { } when { } then { }
import spock.lang.Specification
class AccountSpec extends Specification {
def "crediting empty account results in correct balance"() {
given: "an empty bank account"
def account = new Account(balance: 0)
when: "the account is credited $10"
account << 10.dollars
then: "the account's balance is $10"
account.balance == 10.dollars
}
}
Add methods to the metaClass of current classes (even JDK classes!)
// create a closure called getMeters in the Number meta-class
Number.metaClass.getMeters = {
new Distance(delegate, Unit.METERS)
}
// usage:
100.meters
Easy to write custom AST transformations
Then applied with annotations
Example: @Canonical AST transform built into Groovy
@Canonical
class Person {
String firstName
String lastName
}
// adds constructors with parameters matching fields
user = new Person('Brian', 'Stewart')
// generates toString() method
println user // output: Person(Brian, Stewart)
There are many advanced methods to create sophisticated, highly readable and maintainable domain-specific languages in Groovy, including: