zoukankan      html  css  js  c++  java
  • Groovy 学习手册(4)

    Groovy

    6. 领域特定语言

    Groovy 有许多特性,使它非常适合写DSL(领域特定语言)。这些特性包活:

    • 具有委托机制的闭包;
    • 点号(.)和语句末尾的分号(;)是可选的;
    • 运算符的重载(例如,加号,减号等);
    • methodMissing propertyMissing 方法

    Tip
    关于 DSLs (Domain Specific Languages ),可以参考此书《DSLs In Action》。

    特定领域语言可以用于许多目的,如允许领域专家阅读和编写代码,或澄清业务逻辑的含义。它们允许商业专家阅读或编写代码而不必是一名编程专家。

    1. 委托

    在 Groovy 中,可以将一个代码块(或闭包)作为参数,然后使用一个局部变量作为委托调用它。例如,下面的代码用来发送短信:

    class SMS {
          def from(String fromNumber) {
                // set the from
          }
          def to(String toNumber) {
                // set the to
           }
          def body(String body) {
                // set the body of text
         }
         def send() {
               // send the text.
         }
    }
    

    但是,在 Java 中你需要使用如下的方式:

    SMS m = new  SMS();
    m.from("555-432-1234");
    m.to("555-678-4321");
    m.body("Hey there!");
    m.send();
    

    在 Groovy 中可以定义用来发送短信的静态方法,按照 DSL 风格的使用方式,代码块通常作为一个闭包)。

    def static send(block) {
        SMS m = new SMS()
        block.delegate = m
        block()
        m.send()
    }
    

    这将 SMS 对象设置为代码块的委托,以便将方法转发给它。 有了这个,你现在可以执行以下操作:

    SMS.send {
        from '555-432-1234'
        to '555-678-4321'
        body 'Hey there!'
    }
    

    关于 delegate, 个人理解,闭包中的 delegate 类似于类中的 this.

    2. 重载运算符

    在 Groovy 中,你可以使用运算符的英文单词来重载对应的运算符,例如,plus 指的是“+”运算符,minus 指的是“-”运算符。

    运算符

    除了nextprevious 运算符没有参数外,其他的运算符都有一个参数。下面的例子我们来创建Logic 类包含 一个boolean 类型的变量,来定义andor 方法。

    class  Logic  {
        boolean value
    
        Logic(v) {this.value = v}
        
        def and(Logic other) {
            this.value && other.value
        }
        def or(Logic other) {
            this.value || other.value
        }
    }
    

    我们可以使用这些方法,看是否和我们预想的一样。

    def  pale = new Logic(true)
    def old = new Logic(false)
    
    // Notice that using the built-in && operator 
    // uses “Groovy truth” and returns true 
    // because both variables are non-null .
    println "groovy truth: ${pale && old}" // true
    
    println "using and: ${pale & old}"  // false 
    println "using or: ${pale | old}"  // true
    

    下面的例子定义个重载<<- 的类。

    class Wizards {
        def list = []
        def leftShift(person) { list.add person }
        def  minus(person) { list.remove person }
        String toString() { "Wizards: $list" }
    }
    
    def  wiz = new  Wizards()
    
    wiz << 'Gandolf'
    println wiz
    wiz << 'Harry'
    println wiz
    wiz - 'Harry'
    println wiz
    

    输出结果为:

    Wizards: [Gandolf]
    Wizards: [Gandolf, Harry]
    Wizards: [Gandolf]
    

    你也可以实现 Map 风格的getAtputAt方法的重载,这允许你使用括号的语法方式,如下:

    def value = object[parameter] // uses getAt
    object[parameter] = value // uses putAt
    

    3. 缺失的方法和属性

    以前介绍过,Groovy在运行时提供methodMissing方法了来实现的功能的方式。
    def methodMissing(String name, args)
    然而,Groovy还提供了一种方法来拦截使用Groovy的属性语法访问缺失的属性。使用 propertyMissing(String name)来实现属性的访问,通过propertyMissing(String name, Object value)来修改属性。
    看下面的例子,这里有一个化合物类的DSL片段描述:

    class  Chemistry  {
    	public static void exec(Closure block) {
    		block.delegate = new  Chemistry()
    		block()
    	}
    	
    	def propertyMissing(String name) {
    	 	def  comp = new  Compound(name)
    	 	(comp.elements.size() == 1 && comp.elements.values()[0]==1) ?
    			comp.elements.keySet()[0] : comp
    	}
    }
    

    在这个例子中,propertyMissing 创建一个新的Compound对象并返回,如果Compound对象只有一个元素的话,则返回这个元素对象。这可以用来创建一个基于缺失属性的名字的Compound对象。看例子:

    def  c = new  Chemistry()
    def water = c.H2O
    println water
    println water.weight
    

    这是试图访问一个属性为H2O,会触发 propertyMissing 方法调用。
    通过使用静态 exec 方法,该DSL通过将化学实例作为闭包的委托来实现其全部潜力,这允许以下示例:

    Chemistry.exec {
            def water = H2O
            println water
            println water.weight
    }
    

    这是通过调用ChemistrypropertyMissing 方法相同的效果来创建 H2O 组件。

    Tip
    Chemisty 的完整代码,请访问Chemisty;

    7. trait 语法

    trail 就像具有默认方法和属性的接口,Groovy中的trait 是受到Scale语言里的trait的启发。
    在Java8中我们知道在接口里面可以有默认方法,trail跟Java8中的接口很像,但是具有修改状态(属性)的能力。这样会更加灵活,但是也要非常小心。

    1. 定义trait

    我们来定义一个trait:

    trait Animal {
        int hunger = 100
        def  eat() { println "eating"; hunger -= 1 }
        abstract int getNumberOfLegs()
    }
    

    这个定义的trait具有方法,属性和抽象方法。如果一个类要实现他,必须实现对应的抽象方法。

    2. 使用trait

    要想实现trait,跟Java中的类实现接口一样,使用implements 关键字。

    class Rocket  {
        String name
        def  launch() { println(name + " Take off!") }
    }
    trait MoonLander {
        def land() { println("${getName()} Landing!") }
        abstract String getName()
    }
    class  Apollo  extends  Rocket implements  MoonLander {
    }
    

    你可以如下代码来使用它:

    def  apollo = new  Apollo(name: "Apollo 12")
    apollo.launch()
    apollo.land()
    

    输出结果为:

    Apollo 12 Take off!
    Apollo 12 Landing!
    

    你可以在一个类上实现多个trait,例如:

    trait Shuttle {
        boolean canFly() { true }
        abstract int getCargoBaySize()  
    }
    class MoonShuttle  extends  Rocket
        implements  MoonLander, Shuttle {
        int getCargoBaySize() { 100 }
    }
    

    然后可以如下使用:

    MoonShuttle m = new  MoonShuttle(name: 'Taxi')
    println "${m.name} can fly? ${m.canFly()}"
    println "cargo bay: ${m.getCargoBaySize()}"
    m.launch()
    m.land()
    

    输出结果为:

    Taxi can fly? true
    cargo bay: 100
    Taxi Take off!
    Taxi Landing!
    
  • 相关阅读:
    自动化运维 Expect
    C 语言Struct 实现运行类型识别 RTTI
    Mac 安装配置rz、sz
    Mac Vim + ctags 实现多目录跳转
    Mac 使用Sublime Text 3 搭建C开发环境
    this.$createElement用法
    判断一个数字在数组中出现次数
    iview的table:自定义table表格中列头的标题和点击事件(renderHeader)
    js计算不精确问题
    js 日期时间的格式化
  • 原文地址:https://www.cnblogs.com/IcanFixIt/p/6652369.html
Copyright © 2011-2022 走看看