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: