Skip to end of metadata
Go to start of metadata

ExpandoMetaClass是一个改变你的对象和类的运行时行为的非常方便的方式,却不用编写完备的MetaClass类,它最初是在Grails这把大伞(Grailsumbrella)下开发出来并且整合回Groovy1.5的。每次我们想去改变一个现有的类型的多个属性或方法时,都会有太多的重复的Type.metaClass.xxx。以这个处理运算符重载的单位操作DSL摘录为例:

这里的重复显而易见。但是使用ExpandoMetaClass DSL,我们可以通过重组每个类型的运算符来简化代码:

将一个包含了各种方法和属性定义的闭包作为一个metaClass()方法单独的参数,而不是每一行重复 Type.metaClass。当一个指定的名字只有一个方法时, 用methodName {/* closure / } 格式,但是当有多个方法时,你应当使用追加操作符并按照methodName << { /Closure/ }格式。静态方法也可通过这个机制添加,为了代替传统途径:

你现在也可以这样:

注意这里为了避免构造器看上去像一个静态初始化代码段,你必须引号引上static关键词。对于单个方法的添加,传统的途径显然更简洁,但是当你有多个方法要添加时,EMC DSL更有意义。通过ExpandoMetaClass添加属性至现有类的常见途径是添加一个getter和一个setter方法。例如,假如说你想添加一个方法计算一个文本文件中单词的数量,你可以尝试:

有逻辑存在于getter方法内时,这的确是一个最好的途径,但是当你只是想通过ExpandoMetaClass DSL得到容纳简单值的新属性,那最好还是定义他们吧。在接下来的例子里,添加一个属性lastAccessed到Car类中—它的每个实例都会有这个属性。无论何时调用car实例的某一个方法,这个属性都会被更新为新的时间戳。

在我们的例子中的DSL中,我们通过闭包的委托对象访问那个属性delegate.lastAccessed = new Date()。多亏了invokeMethod()我们拦截了任何方法调用,并为此调用委托调用原始方法,一旦方法不存在则抛出异常。此后,一旦我们调用实例的某个方法,你就可以看到通过执行这个脚本lastAccessed被更新了。

  • No labels