Groovy 学习手册(4)

摘要:
在Groovy中,您可以定义发送SMS消息的静态方法。闭包中的委托与此类似。2.班上超载操作员。在Groovy中,下面的示例是创建一个包含布尔类型变量的Logic类。另外value}defor(Logicoin){this.value | | other.value}}我们可以使用这些方法。

Groovy

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 指的是“-”运算符。

运算符

除了nextprevious 运算符没有参数外,其他的运算符都有一个参数。下面的例子我们来创建Logic 类包含 一个boolean 类型的变量,来定义andor 方法。

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}" // true

println "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 wiz
wiz << 'Harry'
println wiz
wiz - 'Harry'
println wiz

输出结果为:

Wizards: [Gandolf]
Wizards: [Gandolf, Harry]
Wizards: [Gandolf]

你也可以实现 Map 风格的getAtputAt方法的重载,这允许你使用括号的语法方式,如下:

def value = object[parameter] // uses getAt
object[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.H2O
println water
println water.weight

这是试图访问一个属性为H2O,会触发 propertyMissing 方法调用。
通过使用静态 exec 方法,该DSL通过将化学实例作为闭包的委托来实现其全部潜力,这允许以下示例:

Chemistry.exec {
        def water = H2O
        println water
        println water.weight
}

这是通过调用ChemistrypropertyMissing 方法相同的效果来创建 H2O 组件。

Tip
Chemisty 的完整代码,请访问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? true
cargo bay: 100
Taxi Take off!
Taxi Landing!

免责声明:文章转载自《Groovy 学习手册(4)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vscode如何调试node项目(给node项目打断点)INNO Setup 使用笔记下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java架构-一些设计上的基本常识

最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助, 把暂时想到的几条,先记在这里。 1、API与SPI分离 框架或组件通常有两类客户,一个是使用者,一个是扩展者。 API(Application Programming Interface)是给使用者用的, 而SPI(Service Provide Interface)是给扩展者用的。 在设...

IDEA中集成gitee插件

我的IDEA版本为2018.3.4,不同的IDEA版本操作相差无几,请参照下文开始使用Gitee插件吧!   IDEA版本 安装Gitee插件 【File】-【Settings】-【Plugins】,查看gitee插件并安装   安装Gitee插件 安装完成后,重启IDEA   重启IDEA 添加Gitee账户 【File...

一、TestNG测试Case注解

TestNG常用注解有: @BeforeTest/@AfterTest : 一个测试类中,在所有@Test整体执行之前/之后,仅运行一次。 @BeforeMethod/@AfterMethod : 一个测试类中,在每个@Test方法执行之前/之后,运行一次。 @Test : 测试Case,执行顺序按照每个case方法名称的ASCII表的顺序来执行,所以建议...

自动化运维工具puppet详解(二)

一、class 类   1)什么是类?   类是puppet中命名的代码模块,常用于定义一组通用目标的资源,可在puppet全局调用;  类可以被继承,也可以包含子类;  具体定义的语法如下: class NAME{ ... puppet code ... }   其中,在我们定义的时候,需要注意的是: 类的名称只能以小写字母开头,可以包含小字字母、...

SpringMVC类型转换、数据绑定

SpringMVC类型转换、数据绑定详解[附带源码分析] 目录 前言 属性编辑器介绍 重要接口和类介绍 部分类和接口测试 源码分析 编写自定义的属性编辑器 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一。  如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0...

图片上传——用一般处理程序实现

.Net 中如何实现图片上传 1、表单元素使用文件选择框<input  type ="file " />控件2、表单设置enctype="multipart /form-data ",本质上是设置浏览器提交表单的数据时,使用随机分隔符来分 割不同控件的数据,而且数据的组织形势由name=value 换成了数据头和数据体的方式 二、服务器端1、服...