第一篇:groovy對DSL的語法支持

引子

咱們用一段gradle的腳本作引子,理解這一段腳本與通常的groovy代碼是怎麼聯繫起來的html

buildscript {
    repositories {
        jcenter()
        mavenLocal()
        //或者使用指定的本地maven 庫
        maven{
            url "file://D:/repo"
        }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'
    }
}

  

DSL的定義 java

DSL(Domain Specific Language)定義:針對某一領域,具備受限表達性的一種計算機程序設計語言android

 

所謂針對某一領域,其基本思想是「求專不求全」,不像通用目的語言那樣目標範圍涵蓋一切軟件問題,而是專門針對某一特定問題的計算機語言。編程

 

DSL伴隨語義模型出現,語義模型會表現爲程序庫或者框架,對於構建DSL而言,語義模型不可或缺。DSL只是位於其上的一層而已。定義作什麼,而不是用一堆命令語句來描述怎麼作,因此它是聲明式編程(如SQL),這一點很重要。DSL的受限表達性可使DSL語言不易出錯,即使出錯,也易於發現。這是受限表達性的意義。閉包

 

DSL是通用語言的特定用法。內部DSL一般是一段合法的程序,可是具備特定的風格。並且只用到了語言一部分特性。防止DSL逐漸演變爲一種通用語言,要受限表達。目的防止DSL過於複雜,可維護性下降,學習成本提高,偏離方向。不要讓DSL讀起來向天然語言。它是程序語言,比天然語言更加準確和簡潔。語義模型位於語言和DSL之間,爲兩者解耦。DSL腳本,解析器,語義模型,模型——DSL自上而下幾個層次。框架

 

Groovy語法對DSL的支持

咱們先看一個簡單的類和一個奇葩的語法現象:分號省略;默認public省略;有形參的方法調用,括號能夠省略;返回的return能夠省略,默認最後一行代碼的值返回。maven

import groovy.xml.* 
import java.io.* 

class Task{
	//默認省略public和分號
	String summary
	String description
	Date dueDate
	Map m
	
	static void main(args){
		//默認有set和get方法
		Task task1 = new Task()
		task1.setSummary("this is Task1")
		println task1.getSummary()
		//有形參的方法調用,括號能夠省略
		task1.setDescription "this is Task class"
		task1.printDescription ""
		//能夠直接傳map
		Task task3 = new Task()
		task3.setM (['summary':'this is Task3','description':'Task'])
		println task3.getM()
		//map的分號能夠省略
		Task task2= new Task('summary':'this is Task2','description':'Task')
		println task2.getSummary()
		//括號也能夠省略
		Task task4 = new Task()
		task4.setM 'summary':'this is Task4'
		println task4.getM()
	}
	
	public void printDescription(def str){
		println "the task description is : $description" 
	}
}

  

 

看完省略括號的語法現象,下面看另外一個重量級的語法現象——閉包函數

閉包是用{符號括起來的代碼塊,它能夠被單獨運行或調用,也能夠被命名。相似‘匿名類’或內聯函數的概念。學習

閉包中最多見的應用是對集合進行迭代,下面定義了3個閉包對map進行了迭代:gradle

       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中沒有指針)。其次,閉包也能夠不命名(固然做爲代價,只能在定義閉包時執行一次),而方法不能夠。

 

當閉包遇到括號省略,一切都不同了

Project.groovy

public class Project{
    Date date

    void setDateFormat(Closure formatDate){
       println formatDate(date)
    }
}

Main.groovy 

public class Main{

    public static void main(def args){
       Project p = new Project()
       p.setDate new Date()
       //正常
       p.setDateFormat({
           return it.format('yyyy-MM-dd HH:mm:ss')
       })

       //減return
       p.setDateFormat {
           it.format('yyyy-MM-dd HH:mm:ss')
       }

       //減括號(是否是很像咱們的gradle腳本?)

       p.setDateFormat {
           it.format 'yyyy-MM-dd HH:mm:ss'
       }

    }

}

  

減完以後,像不像下面的腳本?

 

 dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'
    }

  

 

惟一的區別是咱們須要用對象來引用方法,其實去掉對象也不難,本身調用本身的方法就能夠了,看下面的代碼:

public class Project{
	Date date
	
	void setDateFormat(Closure formatDate){
		println formatDate(date)
	}
	
	//將format內置
	String format(String f){
		date.format(f)
	}
	//無形參的方法
	void showDate(){
		print date.toString()
	}
	
	void run(){
		//對象去掉,是否是如出一轍了?
		setDateFormat{
			println 'this is a scrip'
			format 'yyyy-MM-dd HH:mm:ss'
			//沒有形參的話就只能乖乖寫括號了
			showDate()
		}
	}
}

 

DSL的兩個關鍵點,某一領域,gradle只爲編譯,也只用於編譯。受限表達,通常只調用腳本運行上下文環境中的方法,爲的就是儘可能簡單,出錯的話,排錯方便。伴隨而生的語義模型就是那一套編譯框架。

Groovy對DSL的支持,表現爲能夠省略:分號,調用方法的括號,return,默認public等。

 

參考:

http://docs.groovy-lang.org/latest/html/documentation/

相關文章
相關標籤/搜索