1. 引子
我們用一段gradle的腳本做引子,了解這一段腳本與一般的groovy代碼是怎麼聯系起來的
buildscript {
repositories {
jcenter()
mavenLocal()
//或者使用指定的本地maven 庫
maven{
url "file://D:/repo"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
2 .DSL的定義
DSL(Domain Specific Language)定義:針對某一領域,具有受限表達性的一種計算機程式設計語言。
所謂針對某一領域,其基本思想是“求專不求全”,不像通用目的語言那樣目标範圍涵蓋一切軟體問題,而是專門針對某一特定問題的計算機語言。
DSL伴随語義模型出現,語義模型會表現為程式庫或者架構,對于建構DSL而言,語義模型不可或缺。DSL隻是位于其上的一層而已。定義做什麼,而不是用一堆指令語句來描述怎麼做,是以它是聲明式程式設計(如SQL),這一點很重要。DSL的受限表達性可以使DSL語言不易出錯,即便出錯,也易于發現。這是受限表達性的意義。
DSL是通用語言的特定用法。内部DSL通常是一段合法的程式,但是具有特定的風格。而且隻用到了語言一部分特性。防止DSL逐漸演變為一種通用語言,要受限表達。目的防止DSL過于複雜,可維護性降低,學習成本提升,偏離方向。不要讓DSL讀起來向自然語言。它是程式語言,比自然語言更加準确和簡潔。語義模型位于語言和DSL之間,為二者解耦。DSL腳本,解析器,語義模型,模型——DSL自上而下幾個層次。
3. Groovy文法對DSL的支援
我們先看一個簡單的類和一個奇葩的文法現象:分号省略;預設public省略;有形參的方法調用,括号可以省略;傳回的return可以省略,預設最後一行代碼的值傳回。
class L1_Task {
//定義entity預設省略public
String summary
String description
Date dueDate
Map taskMap
static void main(String[] args) {
//預設有set和get方法
L1_Task task1=new L1_Task()
task1.setSummary("this is summary1")
println task1.getSummary()
//有形參的方法調用,括号可省略
task1.setDescription "this is desc1"
task1.printDesc "desc1"
//可以直接傳Map
L1_Task task2=new L1_Task()
task2.setTaskMap('name':'zhangsan','company':'feifie')
println task2.getTaskMap()
task2.setTaskMap(['name':'zhangsan2','company':'feifei2'])
println task2.getTaskMap()
//指派
L1_Task task3=new L1_Task('summary':'this is summary3','description':'desc3')
println task3.getSummary()
//括号可省略
L1_Task task4=new L1_Task()
task4.setTaskMap 'name':'mini'
println task4.getTaskMap()
}
void printDesc(def str){
println "printDesc ${str}"
}
}
看完省略括号的文法現象,下面看另一個重量級的文法現象——閉包
閉包是用{符号括起來的代碼塊,它可以被單獨運作或調用,也可以被命名。類似‘匿名類’或内聯函數的概念。
閉包中最常見的應用是對集合進行疊代,下面定義了3個閉包對map進行了疊代:
map.each({key,value-> //key,value兩個參數用于接受每個元素的鍵/值
println "$key:$value"})
map.each{println it} //it是一個關鍵字,代表map集合的每個元素
map.each({ println it.getKey()+"-->"+it.getValue()})
除了用于疊代之外,閉包也可以單獨定義:
def say={word->
println "Hi,$word!"
}
調用:
say('groovy')
say.call('groovy&grails')
輸出:
Hi,groovy!
Hi,groovy&grails!
看起來,閉包類似于方法,需要定義參數和要執行的語句,它也可以通過名稱被調用。然而閉包對象(不要奇怪,閉包也是對象)可以作為參數傳遞(比如前面的閉包作為參數傳遞給了map的each方法)。而在java中,要做到這一點并不容易(也許C++中的函數指針可以,但不要忘記java中沒有指針)。其次,閉包也可以不命名(當然作為代價,隻能在定義閉包時執行一次),而方法不可以。
當閉包遇到括号省略,一切都不一樣了
L2_Project.groovy
class L2_project {
Date date
void setDateFormat(Closure closure){
println closure(date)
}
}
L2_Main.groovy
class L2_Main {
static void main(String[] args) {
L2_project project=new L2_project()
project.setDate new Date() //省略括号
//正常
project.setDateFormat({
// return date.format('yyyy-MM-dd') //date是參數名,見L2_project定義
return it.format('yyyy-MM-dd') //如果隻有一個參數的話,可采用預設參數名it
})
//去掉return 外側小括号
project.setDateFormat {
it.format('yyyy-MM-dd')
}
//去小括号
project.setDateFormat {
it.format 'yyyy-MM-dd'
}
}
}
減完之後,像不像下面的腳本?
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
唯一的差別是我們需要用對象來引用方法,其實去掉對象也不難,自己調用自己的方法就可以了,看下面的代碼:
class L3_Project {
Date date
void setDateFormat(Closure closure){
println closure(date)
}
//将format方法内置
String toFormat(String f){
date.format(f)
}
//無形參定義
void showDate(){
println date.toString()
}
void hanlderToGradle(){
/*
project.setDateFormat去掉對象 成 setDateFormat
*/
setDateFormat {
toFormat "yyyy-MM-dd"
//如果沒有形參,要寫括号
showDate()
}
}
}
DSL的兩個關鍵點,某一領域,gradle隻為編譯,也隻用于編譯。受限表達,一般隻調用腳本運作上下文環境中的方法,為的就是盡量簡單,出錯的話,排錯友善。伴随而生的語義模型就是那一套編譯架構。
Groovy對DSL的支援,表現為可以省略:分号,調用方法的括号,return,預設public等。
轉自: https://www.cnblogs.com/chenjie0949/p/4755389.html 略有修改。如有不妥,敬請告知!