Groovy 使用 GPath 来查询对象

我们通过反射 API 探讨 groovy,目标是获取一个当前对象的所有 getter 方法的简短列表,我们将一步一步的做,因此请打开 groovyConsole 并且跟随向前,你将试图获取到当前对象的信息,输入

this

并且运行脚本(输入 Ctrl-Enter),在输出窗口,你将看到像这样的一些信息

[email protected]

这是当前对象的字符串表示结果,为了获取到当前对象的 class 的相关信息,你能够使 用 this.getClass,但是在 groovy 中你能输入

this.class

显示的结果为(在你运行该脚本之后)

class Script2

通过 getMethods 来获取到 class 对象暴露的方法列表,因此输入:

this.class.methods

结果输出了一个大的方法对象描述列表,这里有太多的信息,你仅仅对方法的名称感兴趣,每一个方法对象有 getName 方法,因此这样调用

this.class.methods.name

并且得到一个方法名称的列表,作为一个字符串对象的列表返回,你可以容易的使用学 到的字符串,正则表达式,列表等相关知识进行工作。由于你仅仅对 getter 方法感兴趣,并且想对这些方法排序,输入

this.class.methods.name.grep(~/get.*/).sort()

你将得到这样的结果

[“getBinding”, “getClass”, “getMetaClass”, “getProperty”]

例如这样的表达式叫做 GPath,一个特殊的事情是你能够在一个方法对象的列表上调用 name 属性并且接收到一个字符串对象的列表。

后台规则是:

list.property

等价于

list.collect{ item -> item?.property }

这是在列表中进行属性访问的特殊用法的缩写,一般用法为:

list*.member

这里*.叫做展开点操作符,并且 member 能够作为属性被访问,一个属性访问,或者 一个方法调用,展开点操作符在方法应用到列表中的所有元素上的时候是需要的,而不是应用到列表本身上,它的等价物为:

list.collect{ item -> item?.member }

为了在实战中明白 GPath,我们看一个适当接近真实情况的例子,假如你正在处理一组发票,每一组引用到固定产品,一个产品有一个价格和名称。

一张发票看起来像表 7.3

图 7.1 使用 UML 类图描述了相应的软件模型,Invo ice 类包含多个指向产品的 LineItem。

列表 7.23 是设计的 groovy 实现,定义类为 groovyBean,通过这个结构构建简单的发票,并且最后使用 GPath 表达式通过多种途径查询对象图。

在列表 7.23 的问题是复杂难懂,首先,在(1),找到每一张发票的总价,这是将所有的项目累加起来。然后我们在(2)查询项目总费用超过 7000 元的所有产品的名称,最后,在(3)查找包含购买了 ULC 产品的每一张发票的日期,并且将它们转换为字符串。

打印出完全的 java 等价实现代码将需要超过 4 页内容并且阅读是非常无趣的,如果你想阅读这个代码,你能在本书的在线资源中找到。

Gpath 和相应的 java 代码的比较是有趣的,Gpath:invoices.items.grep{ it.total() > 7000 }.product.name相应的 java 代码为:[code]// Java

private static List getProductNamesWithItemTotal(Invoice[]

invoices) {

List result = new LinkedList();

for (int i = 0; i < invoices.length; i++) { List items = invoices[i].getItems();
for (Iterator iter = items.iterator(); iter.hasNext():wink: { LineItem lineItem = (LineItem) iter.next();
if (lineItem.total() > 7000){

result.add(lineItem.getProduct().getName());

}

}

}

return result;

}
[/code]表 7.4 给出了两个版本的一些材料,比较代码的行数(LOC),语句数,及嵌套的复杂程度。

也许可以对 java 版本的代码进行裁剪,但是还是有数量级的差别:groovy 需要的代码量至少比 java 少 25%,并且语句少 10%!

写更少的代码不仅仅是一个实践的理由,这也意味着犯更少的错误和更少的测试工作。

而有些新开发人员考虑的是一天写多少行代码,而我们考虑的是一天完成的功能。

在许多语言中,较少的代码导致意思的不明确,但在 groovy 中不是这样,Gpath 的例子是最好的证明,它比 java 对应物更容易阅读和理解,即使是复杂的逻辑。

最后,考虑一下可维护性,假设你的客户完善他们的需求,并且你需要改变查找逻辑。