ObjectGraphBuilder

ObjectGraphBuilder是一个生成器(builder),它可以用来生成符合JavaBean协议的bean的任意的图,它非常有用,譬如可以用来创建测试数据。

比如说你的领域模型包含了下面的类:

package com.acme
 
class Company {
   String name
   Address address
   List employees = []
}
 
class Address {
   String line1
   String line2
   int zip
   String state
}
 
class Employee {
   String name
   int employeeId
   Address address
   Company company
}

通过ObjectGraphBuilder生成一个有3个Employee的Company就这么容易:

def builder = new ObjectGraphBuilder()
builder.classNameResolver = "com.acme"
 
def acme = builder.company( name: 'ACME' ){
   3.times {
      employee( id: it, name: 'Drone ${it}' )
   }
}
 
assertNotNull acme
assert acme.employees.size() == 3

下面描述了在后台发生了什么事情:

  1. 生成器会尝试把一个节点名称匹配为一个Class,并使用默认的ClassNameResolver策略,而这个策略需要一个包名。
  2. 然后上述类的一个实例会被创建,使用一个默认的NewInstanceResolver策略,这个策略会调用一个无参构造函数。
  3. 嵌套的节点的父子关系需要被解析,由于另外两个策略会起作用,所以这里会变得有点复杂。RelationNameResolver会传入父类中子类属性的名称,和子类中父类属性的名称(如果有的话,在这个例子中,Employee有一个父类属性,且恰当的命名为“company”)。ChildPropertySetter会把子类“插入”到父类中,并考虑子类是否属于一个Collection(在这个例子中,employees应该是Company中Employee实例的一个列表)。

所有这4个策略都有一个默认的实现,如果代码符合编写JavaBeans的普遍的协议的话,那么这些实现都会如你期望的运行。但是,如果万一你的bean没有符合协议,那么你可以使用你自己对各个策略的实现。每个策略的设置器(setter)都是闭包友好的,例如

builder.newInstanceResolver = { klass, attributes ->
   if( attributes.foo ){
      return klass.newInstance( [attributes.foo] as Object[] )
   }
   // 默认无参构造函数
   klass.newInstance()
}

ObjectGraphBuilder支持为每个节点提供id,就像SwingBuilder那样,这意味着你可以在生成器中“存储”一个节点的引用,这对于把一个实例关联到其他多个实例来说非常有用。因为一个名为“id”的属性在一些领域模型中可能有业务含义,所以ObjectGraphBuilder包含了一个名为IdentifierResolver的策略,你可以设置它来改变默认的名称(“id”)。相同的情况可能会出现在另外一个属性上,这个属性用来引用一个预先保存的实例,一个名为ReferenceResolver的策略可以用来设置适当的值(默认为“refId”):

def company = builder.company( name: 'ACME' ) {
   address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )
   employee(  name: 'Duke', employeeId: 1, address: a1 )
}
def company = builder.company( name: 'ACME' ) {
   address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )
   employee(  name: 'Duke', employeeId: 1 ){
      address( refId: 'a1' )
   }
}

值得指出的一点是,你不可以修改一个被引用的bean的属性。

在ObjectGraphBuilder无法定位你的类(当你使用groovyConsole来运行一个脚本的时候就会发生)的这种极少出现的情况下,你可以为ObjectGraphBuilder定义一个classLoader来解析类。例如,尝试在groovyConsole中运行下面的代码,然后把classLoader属性注释掉。

class Conference {
   String name
   List speakers = []
}
 
class Speaker {
   String name
}
 
def ogb = new ObjectGraphBuilder( classLoader: getClass().classLoader )
def j1 = ogb.conference( name: 'JavaOne') {
   speaker( name: 'Duke' )
}
assert j1.speakers.size() == 1
assert j1.speakers[0].name == 'Duke'
 
wiki/user_guide/objectgraphbuilder.txt · 最后更改: 2008-10-21 10:23 由 johnny
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki