天天看點

[1/3]groovy對DSL的文法支援

轉自:http://www.cnblogs.com/chenjie0949/p/4755389.html

[size=large][b]引子[/b][/size]

我們用一段gradle的腳本做引子,了解這一段腳本與一般的groovy代碼是怎麼聯系起來的

[size=large][b]DSL的定義[/b][/size]

[b]DSL(Domain Specific Language)定義[/b]:針對某一領域,具有受限表達性的一種計算機程式設計語言。

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

DSL伴随語義模型出現,語義模型會表現為程式庫或者架構,對于建構DSL而言,語義模型不可或缺。DSL隻是位于其上的一層而已。定義做什麼,而不是用一堆指令語句來描述怎麼做,是以它是聲明式程式設計(如SQL),這一點很重要。DSL的受限表達性可以使DSL語言不易出錯,即便出錯,也易于發現。這是受限表達性的意義。

DSL是通用語言的特定用法。内部DSL通常是一段合法的程式,但是具有特定的風格。而且隻用到了語言一部分特性。防止DSL逐漸演變為一種通用語言,要受限表達。目的防止DSL過于複雜,可維護性降低,學習成本提升,偏離方向。不要讓DSL讀起來向自然語言。它是程式語言,比自然語言更加準确和簡潔。語義模型位于語言和DSL之間,為二者解耦。DSL腳本,解析器,語義模型,模型——DSL自上而下幾個層次。

[size=large][b]Groovy文法對DSL的支援[/b][/size]

我們先看一個簡單的類和一個奇葩的文法現象:分号省略;預設public省略;有形參的方法調用,括号可以省略;傳回的return可以省略,預設最後一行代碼的值傳回。

看完省略括号的文法現象,下面看另一個重量級的文法現象——閉包

閉包是用{符号括起來的代碼塊,它可以被單獨運作或調用,也可以被命名。類似‘匿名類’或内聯函數的概念。

閉包中最常見的應用是對集合進行疊代,下面定義了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中沒有指針)。其次,閉包也可以不命名(當然作為代價,隻能在定義閉包時執行一次),而方法不可以。

當閉包遇到括号省略,一切都不一樣了

Project.groovy

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等。

繼續閱讀