程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Groovy >> Groovy探索之MOP 十五 方法名的動態性

Groovy探索之MOP 十五 方法名的動態性

編輯:Groovy

到目前為止,我們的《Groovy探索之MOP》系列已經談到了使用ExpandoMetaClass的方方面面,但值得注意的是,我們通過ExpandoMetaClass給一個類在運行期內添加一個方法,不管是普通方法還是靜態方法,我們都是添加一個確定方法名的方法。即我們添加一個方法名為A的方法,然後才能使用這個方法A。

然而,方法名的動態性,其實是我們早已接觸過的事情,比如在《Groovy探索之invokeMethod方法》裡,我們就可以創建形如“sortByXxxxx()”這樣漂亮的方法。當然了,在那篇文字裡,我們是通過“invokeMethod”方法來實現的。

我們都知道,使用ExpandoMetaClass在運行期內給一個對象或類添加一個方法要比hook方法簡單得多,所以,我們在實際的編碼過程中,更多時候會用到ExpandoMetaClass來實現。那麼,我們使用ExpandoMetaClass是否也可以實現上面hook方法所實現了的方法名的動態性呢?

我們知道,使用ExpandoMetaClass在運行期內添加一個方法(不管是給對象還是給類),形式都是像下面的樣子:

類名.metaClass.方法名 = 方法體

同時,上面的公式還可以寫成如下的樣子:

 

類名.metaClass. "方法名" = 方法體

這下我們明白了,既然方法名可以用雙引號括起來,那麼,如果雙引號裡面是一個Gstring對象的話,這樣,我們就可以給方法賦予一個可變的數據了。從而達到了我們使用ExpandoMetaClass實現方法名的動態性的目的了。

廢話少說,我們還是先來看一個例子吧!

現在,我們有一個T類,如下:

class T
{

}

 

我們就可以這樣對T類添加方法:

String functionName = "a";

T.metaClass."$functionName" = {
println "invoke $functionName"
}

def t = new T()

t.a()

我們在添加方法的時候,使用了一個Gstring對象來代替了寫死的字符串,這樣就實現了方法名的動態性。運行結果為:

invoke a

當然了,你可能會覺得上面的方法名的動態性還不夠清晰,那麼,我們可以實現如下的一個靜態方法,來達到我們方法名動態性的目的:

def static add(functionName)
{
T.metaClass."$functionName" = {
println "invoke $functionName"
}
}

 

這樣,我們就可以如下來使用它了:

add('b')

def t1 = new T()

t1.b()


add('c')

def t2 = new T()

t2.c()

 

運行結果為:

invoke b
invoke c

上面的例子只是小兒科,我們使用ExpandoMetaClass實現的方法名的動態性同樣可以使用於《Groovy探索之invokeMethod方法》中的那個有名的例子。關於那個例子的來歷,大家可以在原文中看到,這裡就不再重述。

在那篇文字中,Student類是這樣的:

class Student {

String no;
String name;
float chinScore;
float mathScore;
float englScore;
float physScore;
float chemScore;
float totalScore;

}

同樣,我們的那個排序工具類還是保留,只是不再實現它的排序方法了,如下:

class SortHelper
{

def list

public SortHelper(list)
{
this.list = list
}

}

 

當然,我們的排序方法是要留給ExpandoMetaClass來實現的,下面就是:

['chinScore','mathScore','englScore','physScore','chemScore','totalScore'].each{
name ->
def name1 = name[0].toUpperCase()+name[1..-1]
SortHelper.metaClass."sortBy${name1}" = {
->
def comparator = {
node1,node2 ->
return node1."${name}".compareTo(node2."${name}")
} as Comparator

Collections.sort(delegate.list,comparator)
}
}

 

我們的想法就是,把要實現的方法的方法名(或部分)組成一個數組,然後依次遍歷,交給ExpandoMetaClass來添加方法。

上面的代碼都很簡單,在這裡我們就不再多說了。

最後,我們來寫代碼測試上面的添加方法了。如下:

 

List scores = [new Student(no:'123',name:'Tom',chinScore:90,mathScore:99,englScore:60,physScore:88,chemScore:96)]
scores<<new Student(no:'124',name:'Mike',chinScore:88,mathScore:90,englScore:90,physScore:98,chemScore:87)
scores<<new Student(no:'125',name:'Alice',chinScore:100,mathScore:55,englScore:98,physScore:67,chemScore:56)

def sorter = new SortHelper(scores)
sorter.sortByChinScore()

scores.each{
println it.name
}

 

運行結果為:

Mike
Tom
Alice

我們都記得我們的初中數學裡學過的對數的概念:對數裡面有一個底的概念,比如我們在程序裡把以2為底的對數寫成如下的樣子:

log2(8)

即以2為底8的對數,這樣,我們可以以任何一個自然數為底,但我們最常用的是以10為底的對數,即:

log10(100)

以10為底的對數,我們通常寫成:

lg(100)

問題為:我們在某種情況下,可能會計算以1到100之間的任何一個數為底的對數。按照上面的分析,其實,就是我們需要給某個工具類100個計算對數的方法。

這就是方法名的動態性的使用了。

我們還是先給出這個工具類來:

class Util
{

}

這個工具類什麼也都沒有實現,留給我們在運行期內動態添加方法,如下:

(1..101).each{
int base->
Util.metaClass.'static'."log$base" = {
int n -> Math.log(n) / Math.log(base)
}
}

它是把1到100進行遍歷,產生了一百個方法,至於如何計算各個底的對數的方法,那是數學公式的問題,我們在這裡不再詳述。

有了上面的代碼,我們現在就可以寫測試代碼了:

println Util.log20(400)

println Util.log100(100)

 

運行結果為:

2.0
1.0

現在,我們可以看到,我們的這些對數方法是不是寫得很漂亮?

在Groovy語言中,動態委派技術永遠是我們最感興趣的技術。而這種動態委派技術的實現,其實也需要用到我們使用ExpandoMetaClass類來實現的動態方法名。

下面就來說一說如何實現動態委派技術。

這是一個簡單的委派類:

class Delegator {

private targetClass

private delegate

Delegator(targetClass, delegate) {

this.targetClass = targetClass

this.delegate = delegate

}

def delegate(String methodName) {

delegate(methodName, methodName)

}

def delegate(String methodName, String asMethodName) {

targetClass.metaClass."$asMethodName" = delegate.&"$methodName"

}

}

這個類十分的簡單,可以明顯的看到“delegate(String methodName, String asMethodName)”方法就是使用的ExpandoMetaClass類實現的動態方法名,如下:

targetClass.metaClass."$asMethodName" = delegate.&"$methodName"

現在,我們就來使用這個動態委派技術。

下面,我們有一個A類:

public class A{

def a()
{
println 'invoke a function...'
}

}

它就是我們需要委派的原始類。下面是一個簡單的B類,沒有任何的方法:

public class B{

}

下面就可以寫測試代碼了:

def a = new A()

def delegator = new Delegator(B,a)

delegator.delegate 'a'

delegator.delegate 'a','b'

delegator.delegate 'a','c'

def b = new B()

b.a()

b.b()

b.c()

 

在上面的代碼中,我們分別把A類的“a”方法委派給了B類的三個方法:“a”、“b”和“c”方法。

最後,我們在B類對象中調用這三個方法。運行結果為:

invoke a function...
invoke a function...
invoke a function...

這充分的體現了使用的ExpandoMetaClass類實現的方法名的動態性的特點。

值得注意的是,上面的動態委派技術的實現,需要在我們的Groovy1.5及以上的版本中才能正常編譯。可以確定的是,Groovy1.0版本是不能編譯的。

同時需要說明的是,我的所有文字,除非注明需要Groovy1.5及以上版本,否則都可以在Groovy1.0版本及以上編譯運行。

謝謝!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved