Expando類是Groovy語言中的一個(gè)相當(dāng)有趣的類,它的作用類似于GroovyBean類,但比GroovyBean類更加靈活;同時(shí),它還更類似于Map類,但也比Map類更加靈活。
我們先來看一個(gè)簡(jiǎn)單的例子:
def e = new Expando()
e.name = 'aa'
println e.name
e.age = 123
println e.age
運(yùn)行結(jié)果為:
aa
123
可以看到,e對(duì)象就像一個(gè)Map對(duì)象一樣,可以任意的增加鍵值對(duì),然后存儲(chǔ)起來。也像一個(gè)動(dòng)態(tài)的GroovyBean對(duì)象,它可以不用預(yù)先設(shè)定任何的屬性,一切都可以在使用的時(shí)候增加。
它與Map對(duì)象的不同在于Expando對(duì)象除了可以在運(yùn)行期增加屬性以外,還可以動(dòng)態(tài)的增加方法。如:
def person = new Expando()
person.name = 'Alice'
person.age = 18
person.description = {
println """
----------description---------
name: ${person.name}
age: ${person.age}
------------------------------
"""
}
person.description()
在上面的代碼中,我們先給Expando類的對(duì)象person增加了兩個(gè)屬性“name”和“age”,接著又給person對(duì)象增加了一個(gè)方法“description”,最后,代碼執(zhí)行了該方法。運(yùn)行結(jié)果為:
----------description---------
name: Alice
age: 18
------------------------------
可以毫不夸張的說,如果沒有可以給Expando對(duì)象在運(yùn)行期內(nèi)動(dòng)態(tài)增加方法的特點(diǎn),Expando對(duì)象將和Map對(duì)象的作用一模一樣,當(dāng)然也就沒有使用它的必要。正是有了可以動(dòng)態(tài)增加方法的特點(diǎn),使得Expando對(duì)象使用起來比Map對(duì)象更加方便。我們可以在很多地方使用到它。
當(dāng)然,在一般情況下,你完全可以使用Expando對(duì)象來代替Map對(duì)象使用。而我們下面要說的,卻是Expando類比Map類更好用的地方。
在一個(gè)項(xiàng)目中,“增刪改查”是一個(gè)項(xiàng)目的基本功能,其中的查詢我們的一般的處理方法是將從數(shù)據(jù)庫獲取的數(shù)據(jù)放在一個(gè)JavaBean對(duì)象里,然后,再將JavaBean對(duì)象的數(shù)據(jù)顯示在頁面上。
當(dāng)然,上面的情況如果在Groovy項(xiàng)目中,則由JavaBean對(duì)象變成了GroovyBean對(duì)象。
我們假設(shè)有如下的一個(gè)GroovyBean類:
class Person
{
String name
int age
}
我們使用下面的賦值語句來模擬從數(shù)據(jù)庫獲取數(shù)據(jù):
person.name = 'Tommy'
person.age = 10
同時(shí),我們也使用下面的語句來模擬將數(shù)據(jù)顯示在頁面上:
println "name: ${person.name} age: ${person.age}"
在絕大多數(shù)的情況下,使用JavaBean或者GroovyBean對(duì)象來存儲(chǔ)數(shù)據(jù)的解決方案是最直接、最簡(jiǎn)單的方案。但使用JavaBean或者GroovyBean對(duì)象來存儲(chǔ)數(shù)據(jù)有一個(gè)基本的要求,即從數(shù)據(jù)庫查詢出來的數(shù)據(jù)有固定的字段個(gè)數(shù)。恰恰在有一些情況下,從數(shù)據(jù)庫查詢出來的數(shù)據(jù)沒有固定的字段個(gè)數(shù),可能這次查詢出來有三個(gè)字段,而下次查詢出來就有五個(gè)字段。在Java項(xiàng)目中,遇到這種情況,我們一般都用Map對(duì)象來代替JavaBean對(duì)象?,F(xiàn)模擬如下:
Map map = new HashMap()
map.name = 'Tommy'
map.age = '10'
map.each{
print "${it.key}: ${it.value} "
}
println()
因?yàn)镸ap對(duì)象可以增加任意多的鍵值對(duì),原則上可以解決上面的動(dòng)態(tài)字段問題。但是上面的Map對(duì)象解決方案存在一個(gè)問題,即顯示數(shù)據(jù)的順序問題:很多情況下,我們是要求按從數(shù)據(jù)庫取得數(shù)據(jù)的順序來顯示數(shù)據(jù)。如上面的例子中,從數(shù)據(jù)庫中取得的數(shù)據(jù)的順序是先是“name”后是“age”,所以要求在顯示的時(shí)候也是這樣的順序??梢钥吹剑厦娴拇a是不能解決順序的問題。
基于上面的原因,我們?cè)贘ava項(xiàng)目提出來一個(gè)妥協(xié)的解決方案,即從數(shù)據(jù)庫查詢出來的字段以“f1”、“f2”……的形式表示。這樣,我們可以給出如下的解決方法:
Map map = new HashMap()
map.f1 = 'Tommy'
map.f2 = '10'
(1..map.size()).each{
print "f${it}: ${map."f${it}"} "
}
println()
可以看到,這個(gè)妥協(xié)的解決方案勉強(qiáng)解決了順序的問題,但需要從存儲(chǔ)過程或sql語句到Java代碼的一系列改動(dòng)才能達(dá)到的。
現(xiàn)在,在Groovy語言中,我們有了Expando類,就再也不需要這樣的妥協(xié)解決方案了:
def person = new Expando()
person.fields = []
person.addField = {
name ->
person.fields << name
}
person.name = 'Tom'
person.addField('name')
person.sex = 'male'
person.addField('sex')
person.age = 12
person.addField('age')
person.fields.each{
println " ${it}: ${person."${it}"}"
}
前面我們說過,Expando類可以讓我們?cè)谶\(yùn)行期內(nèi)增加方法。在上面的代碼中,我們?cè)黾恿艘粋€(gè)名為“addField”的方法,用來存儲(chǔ)字段的順序。
在接下來的賦值語句中,我們首先把字段存儲(chǔ)在“person”對(duì)象中,接著存儲(chǔ)該字段的順序,即:
person.name = 'Tom'
person.addField('name')
在顯示數(shù)據(jù)的時(shí)候,我們先從“person”對(duì)象的“fields”屬性中取出字段的順序,然后按照這個(gè)順序依次取值。這樣就順序的解決了動(dòng)態(tài)字段顯示的所有問題。
上面代碼的運(yùn)行結(jié)果為:
name: Tom
sex: male
age: 12
與賦值時(shí)候的順序一樣,達(dá)到了我們的目的。
通過了上面的一個(gè)簡(jiǎn)單的例子,我們看到Expando類在解決動(dòng)態(tài)性問題上的靈活性?;谒撵`活性,我們可以在很多地方使用到它。如我們可以使用Expando類來做mock測(cè)試,限于篇幅,我們就不再做闡述了。
我們只需要知道Expando類的實(shí)例可以在運(yùn)行期內(nèi)動(dòng)態(tài)的增加屬性和方法,就可以在項(xiàng)目中使用到它。如果我們需要一個(gè)對(duì)象來動(dòng)態(tài)增加屬性,那么我們可以使用Map對(duì)象或者Expando對(duì)象;如果我們需要一個(gè)對(duì)象,除了能動(dòng)態(tài)的增加屬性,還能動(dòng)態(tài)的增加方法,那么我們必須使用Expando對(duì)象。
我們先來看一個(gè)簡(jiǎn)單的例子:
def e = new Expando()
e.name = 'aa'
println e.name
e.age = 123
println e.age
運(yùn)行結(jié)果為:
aa
123
可以看到,e對(duì)象就像一個(gè)Map對(duì)象一樣,可以任意的增加鍵值對(duì),然后存儲(chǔ)起來。也像一個(gè)動(dòng)態(tài)的GroovyBean對(duì)象,它可以不用預(yù)先設(shè)定任何的屬性,一切都可以在使用的時(shí)候增加。
它與Map對(duì)象的不同在于Expando對(duì)象除了可以在運(yùn)行期增加屬性以外,還可以動(dòng)態(tài)的增加方法。如:
def person = new Expando()
person.name = 'Alice'
person.age = 18
person.description = {
println """
----------description---------
name: ${person.name}
age: ${person.age}
------------------------------
"""
}
person.description()
在上面的代碼中,我們先給Expando類的對(duì)象person增加了兩個(gè)屬性“name”和“age”,接著又給person對(duì)象增加了一個(gè)方法“description”,最后,代碼執(zhí)行了該方法。運(yùn)行結(jié)果為:
----------description---------
name: Alice
age: 18
------------------------------
可以毫不夸張的說,如果沒有可以給Expando對(duì)象在運(yùn)行期內(nèi)動(dòng)態(tài)增加方法的特點(diǎn),Expando對(duì)象將和Map對(duì)象的作用一模一樣,當(dāng)然也就沒有使用它的必要。正是有了可以動(dòng)態(tài)增加方法的特點(diǎn),使得Expando對(duì)象使用起來比Map對(duì)象更加方便。我們可以在很多地方使用到它。
當(dāng)然,在一般情況下,你完全可以使用Expando對(duì)象來代替Map對(duì)象使用。而我們下面要說的,卻是Expando類比Map類更好用的地方。
在一個(gè)項(xiàng)目中,“增刪改查”是一個(gè)項(xiàng)目的基本功能,其中的查詢我們的一般的處理方法是將從數(shù)據(jù)庫獲取的數(shù)據(jù)放在一個(gè)JavaBean對(duì)象里,然后,再將JavaBean對(duì)象的數(shù)據(jù)顯示在頁面上。
當(dāng)然,上面的情況如果在Groovy項(xiàng)目中,則由JavaBean對(duì)象變成了GroovyBean對(duì)象。
我們假設(shè)有如下的一個(gè)GroovyBean類:
class Person
{
String name
int age
}
我們使用下面的賦值語句來模擬從數(shù)據(jù)庫獲取數(shù)據(jù):
person.name = 'Tommy'
person.age = 10
同時(shí),我們也使用下面的語句來模擬將數(shù)據(jù)顯示在頁面上:
println "name: ${person.name} age: ${person.age}"
在絕大多數(shù)的情況下,使用JavaBean或者GroovyBean對(duì)象來存儲(chǔ)數(shù)據(jù)的解決方案是最直接、最簡(jiǎn)單的方案。但使用JavaBean或者GroovyBean對(duì)象來存儲(chǔ)數(shù)據(jù)有一個(gè)基本的要求,即從數(shù)據(jù)庫查詢出來的數(shù)據(jù)有固定的字段個(gè)數(shù)。恰恰在有一些情況下,從數(shù)據(jù)庫查詢出來的數(shù)據(jù)沒有固定的字段個(gè)數(shù),可能這次查詢出來有三個(gè)字段,而下次查詢出來就有五個(gè)字段。在Java項(xiàng)目中,遇到這種情況,我們一般都用Map對(duì)象來代替JavaBean對(duì)象?,F(xiàn)模擬如下:
Map map = new HashMap()
map.name = 'Tommy'
map.age = '10'
map.each{
print "${it.key}: ${it.value} "
}
println()
因?yàn)镸ap對(duì)象可以增加任意多的鍵值對(duì),原則上可以解決上面的動(dòng)態(tài)字段問題。但是上面的Map對(duì)象解決方案存在一個(gè)問題,即顯示數(shù)據(jù)的順序問題:很多情況下,我們是要求按從數(shù)據(jù)庫取得數(shù)據(jù)的順序來顯示數(shù)據(jù)。如上面的例子中,從數(shù)據(jù)庫中取得的數(shù)據(jù)的順序是先是“name”后是“age”,所以要求在顯示的時(shí)候也是這樣的順序??梢钥吹剑厦娴拇a是不能解決順序的問題。
基于上面的原因,我們?cè)贘ava項(xiàng)目提出來一個(gè)妥協(xié)的解決方案,即從數(shù)據(jù)庫查詢出來的字段以“f1”、“f2”……的形式表示。這樣,我們可以給出如下的解決方法:
Map map = new HashMap()
map.f1 = 'Tommy'
map.f2 = '10'
(1..map.size()).each{
print "f${it}: ${map."f${it}"} "
}
println()
可以看到,這個(gè)妥協(xié)的解決方案勉強(qiáng)解決了順序的問題,但需要從存儲(chǔ)過程或sql語句到Java代碼的一系列改動(dòng)才能達(dá)到的。
現(xiàn)在,在Groovy語言中,我們有了Expando類,就再也不需要這樣的妥協(xié)解決方案了:
def person = new Expando()
person.fields = []
person.addField = {
name ->
person.fields << name
}
person.name = 'Tom'
person.addField('name')
person.sex = 'male'
person.addField('sex')
person.age = 12
person.addField('age')
person.fields.each{
println " ${it}: ${person."${it}"}"
}
前面我們說過,Expando類可以讓我們?cè)谶\(yùn)行期內(nèi)增加方法。在上面的代碼中,我們?cè)黾恿艘粋€(gè)名為“addField”的方法,用來存儲(chǔ)字段的順序。
在接下來的賦值語句中,我們首先把字段存儲(chǔ)在“person”對(duì)象中,接著存儲(chǔ)該字段的順序,即:
person.name = 'Tom'
person.addField('name')
在顯示數(shù)據(jù)的時(shí)候,我們先從“person”對(duì)象的“fields”屬性中取出字段的順序,然后按照這個(gè)順序依次取值。這樣就順序的解決了動(dòng)態(tài)字段顯示的所有問題。
上面代碼的運(yùn)行結(jié)果為:
name: Tom
sex: male
age: 12
與賦值時(shí)候的順序一樣,達(dá)到了我們的目的。
通過了上面的一個(gè)簡(jiǎn)單的例子,我們看到Expando類在解決動(dòng)態(tài)性問題上的靈活性?;谒撵`活性,我們可以在很多地方使用到它。如我們可以使用Expando類來做mock測(cè)試,限于篇幅,我們就不再做闡述了。
我們只需要知道Expando類的實(shí)例可以在運(yùn)行期內(nèi)動(dòng)態(tài)的增加屬性和方法,就可以在項(xiàng)目中使用到它。如果我們需要一個(gè)對(duì)象來動(dòng)態(tài)增加屬性,那么我們可以使用Map對(duì)象或者Expando對(duì)象;如果我們需要一個(gè)對(duì)象,除了能動(dòng)態(tài)的增加屬性,還能動(dòng)態(tài)的增加方法,那么我們必須使用Expando對(duì)象。

