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