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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值