Groovy与DSL

DSL:领域特定语言。常用于聚焦指定的领域或问题,这就要求 DSL 具备强大的表现力,同时在使用起来要简单。由于其使用简单的特性,DSL 通常不会像 Java,C++等语言将其应用于一般性的编程任务。

由语言的难易程度,做一个简单的排序:配置文件 < DSL < 编程语言(eg:Java,C++...)

  • 配置文件是最简单的一种,常见的有用 xml 来进行配置项目,这需要预先定义好一些规范,只有按照预先定义好的规范书写配置文件,才能得到正确的解析。解析其实也是使用编程语言,通过读文件的形式,按照预先定义的规范,得到相应的配置。
  • 编程语言是最高级的,其本身可以实现条件判断,循环等这样的逻辑,这是配置文件很难实现的。但是很少有用编程语言来直接表述一项任务的,因为其缺乏像配置文件那样简介的语义。
  • DSL 则介于两者之间,即有像配置文件那样清晰的语义,即很强的语义表现力,同时自身也具备一定的逻辑。

然而 DSL 的应用领域非常有限,没有必要单独为其开发一款语言。就像 xml 中的配置文件是通过高级语言解析而来,我们也可以用高级语言解析 DSL 文件,执行相关的逻辑。但是与 xml 配置文件不同的是,xml 配置文件需要预先定义规范。而 DSL 不需要定义规范,其语法受到现有语言的约束,不用使用任何解析器,而是巧妙的讲语法映射到底层语言中的方法和属性,这样使得用户在使用起来可能意识不到自己正在使用的是一门更广泛的语言的语法。

DSL 的两个显著特点:

  • 上下文相关
  • 流畅的调用

鉴于 Groovy 动态特性和元编程能力,其很适合作为 DSL 底层的解析语言。接下来探讨在 Groovy 的基础上构建 DSL。

对于 Groovy 来说,一个伟大的 DSL 产物就是新一代构建工具——Gradle。

利用闭包实现上下文相关性

上下文:就是一个特定的语境,在这个语境下,大家没有必要一直重申这一语境前提,而是所有的逻辑都在这个前提下执行。
看如下 Groovy 代码:

list = [1,2]
list.add(3)
list.add(4)
println list.size()

在上面的代码中,每条语句都是针对 list 的,但是每条语句都重复声明了 list 的语境,未免有些啰嗦,于是 groovy 提供了 with()方法,用来设置上下文,上面的代码可以修改为:

list = [1,2]
list.with{
    add(3)
    add(4)
    println size()
}

这里使用了闭包,将闭包中的语句的上下文路由到 list。原因是 with 中的闭包将其 delegate 设置到了调用 with 的对象上。

同时注意到这里 with 的原型是 with(closure),当一个方法的最后一个参数是闭包,同时我们传入的是一个匿名闭包时,那么其调用的方式就是上述 with 的形式,这不是方法的定义,而是方法的调用,只不过后面的闭包是作为方法的最后一个参数,单独拿出来了。单独拿出来比写在括号内,语法更加简洁。因为花括号后面在跟一个右括号总感觉破坏了代码的优美性。

受到 Groovy 所提供的 with 的启发,我们也可以仿照 with 的实现方式,提供类似的上下文执行环境,实现我们的 DSL。

def addList(closure){
    list = []
    closure.delegate = list
    closure()
    list
}

// 调用 addList 提供的上下文特性,想 list 中添加元素
list = addList(){
    add(1)
    add(2)
    add(3)
    size()
}

println list

通过将闭包的代理进行配置,自然就提供了一个统一的上下文环境,这样我们在闭包中调用的任何方法,都是基于之前设置的代理的,这一方法在 gradle 中是很常见的。

Groovy 针对流畅性的先天优势

流畅性可以提高代码的可读性,使代码自然流动。

Groovy 所具备的天然的优势有:

  • 动态类型和可选类型
  • 动态加载,操作和执行脚本的灵活性
  • 分类和 ExpandoMetaClass 可以扩展已有类的功能
  • 闭包为执行提供很好的上下文
  • 操作符的重载可以使我们自由的定义操作符

灵活的方法调用语法

如果方法是有返回值的,那么在调用时可以不使用点符号(.) 而是用空格将调用者和方法分开,就能在返回的这个实例上进行连续的调用

灵活的括号语法

当调用的方法需要参数时,Groovy 不要求使用括号,若有多个参数,那么参数之间依然使用逗号分隔;如果不需要参数,那么方法的调用必须显示的使用括号。

让无参数的方法调用也不需要括号

为了和有参数的方法调用时不使用括号的语法,统一风格,我们设法让无参数的方法调用也不需要括号。当然这个功能是 Groovy 自身不支持的。直接这样调用,Groovy 会认为这是在访问一个属性,那么我们结合 Groovy 中对属性的访问就是对 getXXX 的访问,将无参数的方法名改成 getXXX 的形式,即可实现“调用无参数的方法不需要括号”的语法!

结合刚刚提到的三点,我们设计一个 DSL 计算器:

value = 0
def getClear() { value = 0}
def add(number) { value += number }
def getTotal() { println "Total is #value" }

//接下来开始使用这一简介的 DSL
clear
add 2
add 5
total

由上面的 groovy 创建的简易加法器的 DSL,可以感受到其强大的流畅性。我们在调用时,甚至都可能意识不到这是代码,而是一些配置文件或者普通的文字描述,但这确实是 Groovy 代码。

接下来提供一组这样的数据,进一步解读 DSL。

jump fast,forward turn right move ahead

上面的依旧是 Groovy 代码,首先是 jump 方法的调用,其接受两个参数 fast 和 forward,该方法的返回值类型上有一个 turn 方法,其接受一个 right 参数,同时方法的返回值类型上有一个 move 方法,其接受一个 ahead 参数。这就是 DSL 强大的表现力。其底层就可以用之前讲到的 Groovy 特性提供技术支持。

MOP 与 DSL

接下来使用 MOP 中的方法合成

detailInfo = [:]
def methodMissing(String name,args){
    detailInfo[name] = args
}

def introduce(closure){
    closure.delegate = this
    closure()
    println "hello,everyone"
    detailInfo.each{
        key,value ->
            println "My $key is $value"
    }
}


introduce{
    name "zx"
    age 18
}

可以看到,利用 Groovy 的 MOP 能力,设计与执行 DSL 将变得非常容易。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,726评论 6 342
  • 前言 由于项目需要用到 Groovy 语言,这两天对其进行了粗略的学习,本文是对学习做的一个简单总结,主要内容参考...
    简单的土豆阅读 188,369评论 12 202
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,139评论 9 118
  • 导语: 随着技术的发展,不管是前端开发、服务端开发或者是移动端开发(移动也是前端的一个分支)中都会用到自动化构建工...
    伊始雨深阅读 3,019评论 0 4