天天看點

Grade 2 --- Groovy對DSL的支援1. 引子2 .DSL的定義   3. Groovy文法對DSL的支援

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   略有修改。如有不妥,敬請告知!

繼續閱讀