6. 领域特定语言
Groovy 有许多特性,使它非常适合写DSL(领域特定语言)。这些特性包活:
- 具有委托机制的闭包;
- 点号(.)和语句末尾的分号(;)是可选的;
- 运算符的重载(例如,加号,减号等);
methodMissing
和propertyMissing
方法
Tip
关于 DSLs (Domain Specific Languages ),可以参考此书《DSLs In Action》。
特定领域语言可以用于许多目的,如允许领域专家阅读和编写代码,或澄清业务逻辑的含义。它们允许商业专家阅读或编写代码而不必是一名编程专家。
1. 委托
在 Groovy 中,可以将一个代码块(或闭包)作为参数,然后使用一个局部变量作为委托调用它。例如,下面的代码用来发送短信:
class SMS { def from(String fromNumber) { // set the from } def to(String toNumber) { // set the to } def body(String body) { // set the body of text } def send() { // send the text. }}
但是,在 Java 中你需要使用如下的方式:
SMS m = new SMS();m.from("555-432-1234");m.to("555-678-4321");m.body("Hey there!");m.send();
在 Groovy 中可以定义用来发送短信的静态方法,按照 DSL 风格的使用方式,代码块通常作为一个闭包)。
def static send(block) { SMS m = new SMS() block.delegate = m block() m.send()}
这将 SMS
对象设置为代码块的委托,以便将方法转发给它。 有了这个,你现在可以执行以下操作:
SMS.send { from '555-432-1234' to '555-678-4321' body 'Hey there!'}
关于 delegate
, 个人理解,闭包中的 delegate
类似于类中的 this
.
2. 重载运算符
在 Groovy 中,你可以使用运算符的英文单词来重载对应的运算符,例如,plus 指的是“+”运算符,minus 指的是“-”运算符。
除了next
和 previous
运算符没有参数外,其他的运算符都有一个参数。下面的例子我们来创建Logic
类包含 一个boolean 类型的变量,来定义and
和 or
方法。
class Logic { boolean value Logic(v) {this.value = v} def and(Logic other) { this.value && other.value } def or(Logic other) { this.value || other.value }}
我们可以使用这些方法,看是否和我们预想的一样。
def pale = new Logic(true)def old = new Logic(false)// Notice that using the built-in && operator // uses “Groovy truth” and returns true // because both variables are non-null .println "groovy truth: ${pale && old}" // trueprintln "using and: ${pale & old}" // false println "using or: ${pale | old}" // true
下面的例子定义个重载<<
和 -
的类。
class Wizards { def list = [] def leftShift(person) { list.add person } def minus(person) { list.remove person } String toString() { "Wizards: $list" }}def wiz = new Wizards()wiz << 'Gandolf'println wizwiz << 'Harry'println wizwiz - 'Harry'println wiz
输出结果为:
Wizards: [Gandolf]Wizards: [Gandolf, Harry]Wizards: [Gandolf]
你也可以实现 Map 风格的getAt
和putAt
方法的重载,这允许你使用括号的语法方式,如下:
def value = object[parameter] // uses getAtobject[parameter] = value // uses putAt
3. 缺失的方法和属性
以前介绍过,Groovy在运行时提供methodMissing
方法了来实现的功能的方式。
def methodMissing(String name, args)
然而,Groovy还提供了一种方法来拦截使用Groovy的属性语法访问缺失的属性。使用 propertyMissing(String name)
来实现属性的访问,通过propertyMissing(String name, Object value)
来修改属性。 看下面的例子,这里有一个化合物类的DSL片段描述: class Chemistry { public static void exec(Closure block) { block.delegate = new Chemistry() block() } def propertyMissing(String name) { def comp = new Compound(name) (comp.elements.size() == 1 && comp.elements.values()[0]==1) ? comp.elements.keySet()[0] : comp }}
在这个例子中,propertyMissing
创建一个新的Compound
对象并返回,如果Compound
对象只有一个元素的话,则返回这个元素对象。这可以用来创建一个基于缺失属性的名字的Compound
对象。看例子:
def c = new Chemistry()def water = c.H2Oprintln waterprintln water.weight
这是试图访问一个属性为H2O,会触发 propertyMissing
方法调用。
exec
方法,该DSL通过将化学实例作为闭包的委托来实现其全部潜力,这允许以下示例: Chemistry.exec { def water = H2O println water println water.weight}
这是通过调用Chemistry
的propertyMissing
方法相同的效果来创建 H2O 组件。
Tip
Chemisty 的完整代码,请访问;
7. trait 语法
trail 就像具有默认方法和属性的接口,Groovy中的trait 是受到Scale语言里的trait的启发。
在Java8中我们知道在接口里面可以有默认方法,trail跟Java8中的接口很像,但是具有修改状态(属性)的能力。这样会更加灵活,但是也要非常小心。1. 定义trait
我们来定义一个trait:
trait Animal { int hunger = 100 def eat() { println "eating"; hunger -= 1 } abstract int getNumberOfLegs()}
这个定义的trait具有方法,属性和抽象方法。如果一个类要实现他,必须实现对应的抽象方法。
2. 使用trait
要想实现trait,跟Java中的类实现接口一样,使用implements 关键字。
class Rocket { String name def launch() { println(name + " Take off!") }}trait MoonLander { def land() { println("${getName()} Landing!") } abstract String getName()}class Apollo extends Rocket implements MoonLander {}
你可以如下代码来使用它:
def apollo = new Apollo(name: "Apollo 12")apollo.launch()apollo.land()
输出结果为:
Apollo 12 Take off!Apollo 12 Landing!
你可以在一个类上实现多个trait,例如:
trait Shuttle { boolean canFly() { true } abstract int getCargoBaySize() }class MoonShuttle extends Rocket implements MoonLander, Shuttle { int getCargoBaySize() { 100 }}
然后可以如下使用:
MoonShuttle m = new MoonShuttle(name: 'Taxi')println "${m.name} can fly? ${m.canFly()}"println "cargo bay: ${m.getCargoBaySize()}"m.launch()m.land()
输出结果为:
Taxi can fly? truecargo bay: 100Taxi Take off!Taxi Landing!