[code generation] 模版引擎_groovy

Groovy:

1. 是基于 JVM 的敏捷的动态语言,它可以使用其他Java语言编写的库,特别适合与Spring的动态语言支持一起使用。

2. 它既可以用于面相对象编程,又可以用作纯粹的脚本语言

3. 具有闭包和动态语言中的其它特性

Groovy 模版引擎:

1. Groovy 的模板引擎框架简化视图编程

(1) Groovy 模板引擎与 XSLT 很类似,可以产生模板定义的任何格式,包括 XML、HTML、SQL 和 Groovy 代码。

(2) Groovy 支持 here-docs 的概念here-doc是创建格式化 String(例如 HTML 和 XML)的一种便利机制。注意 here-doc 语法与普通的 String声明并没有很大的不同,不过三重引号。

清单 2. Groovy 中的 Here-docs
 itext = 
"""
 This is another multiline String 
 that takes up a few lines. Doesn't 
 do anything different from the previous one. 
"""

(3) Groovy 使用 GString来简化运行时替换。简单来说,GString允许您使用 与 bash 类似的${}语法进行替换。GString的优势是您从来都不需要知道自己正在使用的是 GString类型;只需要在 Groovy 中简单地编写 String即可,就仿佛是在 Java 代码中一样。

清单 3. Groovy 中的 GString
 lang = "Groovy"
 println "Uncle man, Uncle man, I dig ${lang}."

运行时替换实际上是动态语言的一个通用特性;与其他情况一样,Groovy 还会更进一步。Groovy 的 GString允许您对替换的值调用 autocall方法,当开始构建动态文本时,这会产生很多变化。例如,在清单 4 中,我可以对指定的变量按照 String对象类型调用一个方法(在本例中是 length()方法)。

清单 4. GString 自动调用
 lang = "Groovy"
 println "I dig any language with ${lang.length()} characters in its name!"


(4)Groovy 模版

使用模版 = 创建模板+提供映射。

创建这些模板的关键在于对那些运行时要替换的变量的定义。例如,在清单 5 中,我们为创建 GroovyTestCase定义了一个模板。

清单 5. 一个创建 GroovyTestCase 的模板
 import groovy.util.GroovyTestCase 
 class <%=test_suite %> extends GroovyTestCase { 
  <% for(tc in test_cases) { 
     println "\tvoid ${tc}() { } "
  }%> 
 }

清单 5 中的模板就类似于一个 JSP 文件,因为我们使用了 <%<%=语法。然而,由于 Groovy 的灵活性很好,因此您并不局限于使用 JSP 语法。您还可以自由使用 Groovy 中杰出的 GString,如清单 6 所示。

清单 6. GString 的使用
 <person> 
  <name first="${p.fname}" last="${p.lname}"/> 
 </person>

在清单 6 中,我创建了一个简单的模板,它表示一个定义 person元素集合的 XML 文档。您可以看到这个模板期望一个具有 fnamelname属性、名为 p的对象。Groovy 是一种简化从模型中分离视图过程的手段。

下一个步骤是编写运行时的映射代码。

我所需要的是一个 映射,其关键字是模板中的变量名,键值是运行时的值。

例如,如果一个简单的模板有一个名为 favlang的变量,我就需要与 favlang键值建立一个 映射。这个键值可以是根据我自己的喜好选择的任何脚本语言(在本例中,当然是 Groovy)。

在清单 7 中,我们定义了这样一个简单的模板,在清单 8 中,我将向您展示对应的映射代码。

清单 7. 用来展示映射的简单代码
 My favorite dynamic language is ${favlang}

清单 8. 为一个简单的模板映射值

 package com.vanward.groovy.tmpl 
 import groovy.text.Template 
 import groovy.text.SimpleTemplateEngine 
 import java.io.File 
 class SimpleTemplate{ 
  static void main(args) { 
    fle = new File("simple-txt.tmpl") 
    binding = ["favlang": "Groovy"] 
    engine = new SimpleTemplateEngine() 
    template = engine.createTemplate(fle).make(binding) 
    println template.toString() 
  } 
 }

这段代码中创建了一个 binding对象;实际上,这就是一个 映射。我将在模板中找到的值 favlang映射到 StringGroovy 上。

清单 8 中的最后一个步骤是打印进程的输出信息。正如您可以看到的一样,创建一个 binding对象并提供正确的映射是件轻而易举的小事,至少在我们这个简单的例子中是如此。在下一节中,我们将会使用一个更加复杂的例子对 Groovy 模板引擎进行测试。

在清单 9 中,创建了一个 Person类来表示在 清单 6中定义的 person元素。

清单 9. Groovy 中的 Person 类
 class Person{ 
 age 
 fname 
 lname 
 String toString(){ 
  return "Age: " + age + " First Name: " + fname + " Last Name: " + lname 
 } 
 }

在清单 10 中,您可以看到对上面定义的 Person类的实例进行映射的代码。

清单 10. 在 Person 类与模板之间建立映射
 import java.io.File 
 import groovy.text.Template 
 import groovy.text.SimpleTemplateEngine 
 class TemplatePerson{ 
  static void main(args) { 
    pers1 = new Person(age:12, fname:"Sam", lname:"Covery") 
    fle = new File("person_report.tmpl") 
    binding = ["p":pers1] 
    engine = new SimpleTemplateEngine() 
    template = engine.createTemplate(fle).make(binding) 
    println template.toString() 
  } 
 }

上面的代码看起来很熟悉,不是吗?实际上,它与 清单 8非常类似,不过增加了一行创建 pers1实例的代码。现在,再次快速查看一下 清单 6中的代码。您看到模板是如何引用属性 fnamelname的了吗?我所做的操作是创建一个 Person实例,其 fname属性设置为“Sam”,属性 lname设置为“Covery”。

在运行清单 10 中的代码时,输出结果是 XML 文件,用来定义 person元素,如清单 11 所示。

清单 11. Person 模板的输出结果
 <person> 
  <name first="Sam" last="Covery"/> 
 </person>

映射一个列表

清单 5中,我为 GroovyTestCase定义了一个模板。现在如果您看一下这个模板,就会注意到这个定义有一些逻辑用于在一个集合上迭代。在清单 12 中,您将看到一些非常类似的代码,不过这些代码的逻辑是用来映射一个测试用例 列表的。

清单 12. 映射测试用例列表
 fle = new File("unit_test.tmpl") 
 coll = ["testBinding", "testToString", "testAdd"] 
 binding = ["test_suite":"TemplateTest", "test_cases":coll] 
 engine = new SimpleTemplateEngine() 
 template = engine.createTemplate(fle).make(binding) 
 println template.toString()

查看一下 清单 5,它显示了模板期望一个名为“test_cases”的 列表—— 在清单 12 中它定义为 coll,包含 3 个元素。我简单地将 coll设置为“test_cases”绑定对象中的键值,现在代码就准备好运行了。

定义模板

现在我将开始定义模板了 —— 它看起来更加类似于想要的输出结果,采用了一些逻辑来循环遍历一组类。

清单 14. 为原来的代码应用模板
 <md5report> 
 <% for(clzz in clazzes) { 
  println "<md5 class=\"${clzz.name}\" value=\"${clzz.value}\"/>"
 }%> 
 </md5report>

清单 14 中定义的模板与为 GroovyTestCase定义的模板类似,其中包括循环遍历一个集合的逻辑。还要注意我在此处混合使用了 JSP 和 GString的语法。

编写映射代码

定义好模板之后,下一个步骤是编写运行时的映射代码。我需要将原来的写入文件的逻辑替换为下面的代码:构建一个 ChecksumClass对象集合,然后将这些对象放到 binding对象中。

这个模型然后就会变成清单 15 中定义的 ChecksumClass

清单 15. 在 Groovy 中定义的 CheckSumClass
 class CheckSumClass{ 
  name 
  value 
  String toString(){ 
   return "name " + name + " value " + value 
  } 
 }


创建集合

接下来,我需要重构刚才写入文件的那段代码 —— 这一次采用一定的逻辑使用 ChecksumClass构造一个列表,如清单 16 所示。

清单 16. 重构代码创建一个 ChecksumClass 的集合
 clssez = [] 
 for(f in scanner){ 
  f.eachLine{ line | 
   iname = formatClassName(bsedir, f.path) 
   clssez << new CheckSumClass(name:iname, value:line) 
  } 
 }

清单 16 显示了使用类 Ruby 的语法将对象添加到 列表中是多么简单 —— 这就是 奇妙的groovy。我首先使用 []语法创建 清单。然后使用简短的 for循环,后面是一个带有闭包的迭代器。这个闭包接受每一个 line(在本例中是一个校验和值),并创建一个新定义的 CheckSumClass实例(使用 Groovy 的自动生成的构造函数),并将二者添加到集合中。

添加模板映射

我需要做的最后一件事情是添加模板引擎特定的代码。这段代码将执行运行时映射,并将对应的格式化后的模板写入原始的文件中,如清单 17 所示。

清单 17. 使用模板映射重构原来的代码
 fle = new File("report.tmpl") 
 binding = ["clazzes": clzzez] 
 engine = new SimpleTemplateEngine() 
 template = engine.createTemplate(fle).make(binding) 
 nfile.withPrintWriter{ pwriter | 
  pwriter.println template.toString() 
 }

现在,清单 17 中的代码对您来说太陈旧了。我利用了清单 16 中的 列表,并将其放入 binding对象。然后读取 nfile对象,并将相应的输出内容从 清单 14中的映射模板写入文件中。

在将这些内容都放入清单 18 之前,您可能希望返回 清单 13最后看一眼开始时使用的那段蹩脚的代码。下面是新的代码,您可以进行比较一下:

清单 18. 看,新的代码!
 /** 
 * 
 */ 
 buildReport(bsedir){ 
 ant = new AntBuilder() 
 scanner = ant.fileScanner { 
   fileset(dir:bsedir) { 
     include(name:"**/*class.md5.txt") 
   } 
 } 
 rdir = bsedir + File.separator + "xml" + File.separator 
 file = new File(rdir) 
 if(!file.exists()){ 
   ant.mkdir(dir:rdir) 
 } 
 nfile = new File(rdir + File.separator + "checksum.xml") 
 clssez = [] 
 for(f in scanner){ 
   f.eachLine{ line | 
    iname = formatClassName(bsedir, f.path) 
    clssez << new CheckSumClass(name:iname, value:line) 
   } 
 } 
 fle = new File("report.tmpl") 
 binding = ["clazzes": clzzez] 
 engine = new SimpleTemplateEngine() 
 template = engine.createTemplate(fle).make(binding) 
 nfile.withPrintWriter{ pwriter | 
   pwriter.println template.toString() 
 } 
 }

回顾一下,我所干的事情不过是将一些蹩脚的 println替换成 Groovy 的更精巧的模板代码。(一些熟悉重构的人可能会说我应该使用 Extract Method进一步对代码进行优化。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值