zoukankan      html  css  js  c++  java
  • 第一篇:groovy对DSL的语法支持

    引子

    我们用一段gradle的脚本做引子,理解这一段脚本与一般的groovy代码是怎么联系起来的

    buildscript {
        repositories {
            jcenter()
            mavenLocal()
            //或者使用指定的本地maven 库
            maven{
                url "file://D:/repo"
            }
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:1.2.3'
        }
    }
    

      

    DSL的定义 

    DSL(Domain Specific Language)定义:针对某一领域,具有受限表达性的一种计算机程序设计语言

    所谓针对某一领域,其基本思想是“求专不求全”,不像通用目的语言那样目标范围涵盖一切软件问题,而是专门针对某一特定问题的计算机语言。

    DSL伴随语义模型出现,语义模型会表现为程序库或者框架,对于构建DSL而言,语义模型不可或缺。DSL只是位于其上的一层而已。定义做什么,而不是用一堆命令语句来描述怎么做,所以它是声明式编程(如SQL),这一点很重要。DSL的受限表达性可以使DSL语言不易出错,即便出错,也易于发现。这是受限表达性的意义。

    DSL是通用语言的特定用法。内部DSL通常是一段合法的程序,但是具有特定的风格。而且只用到了语言一部分特性。防止DSL逐渐演变为一种通用语言,要受限表达。目的防止DSL过于复杂,可维护性降低,学习成本提升,偏离方向。不要让DSL读起来向自然语言。它是程序语言,比自然语言更加准确和简洁。语义模型位于语言和DSL之间,为二者解耦。DSL脚本,解析器,语义模型,模型——DSL自上而下几个层次。

    Groovy语法对DSL的支持

    我们先看一个简单的类和一个奇葩的语法现象:分号省略;默认public省略;有形参的方法调用,括号可以省略;返回的return可以省略,默认最后一行代码的值返回。

    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进行了迭代:

           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/

  • 相关阅读:
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(7)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(6)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(5)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(4)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(3)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(2)
    【互动问答分享】第11期决胜云计算大数据时代Spark亚太研究院公益大讲堂
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第四步)(1)
    【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第三步)(3)
    docker 进程监控 Dumb-Init进程信号处理 --转自https://blog.csdn.net/tiger435/article/details/54971929
  • 原文地址:https://www.cnblogs.com/chenjie0949/p/4755389.html
Copyright © 2011-2022 走看看