方法注入(method injection):编写代码时知道想要添加到一个或多个类中的方法的名字。利用方法注入,可以动态地向类中添加行为。也可以向任意数目的类中注入一组实现某一特定功能的可复用方法。可以通过使用分类,使用ExpandoMetaClass或Groovy的Mixin工具来注入方法。
方法合成(method synthesis):想在调用时动态地确定方法的行为。Groovy的invokeMethod()、methodMissing()和GroovyInterceptable对于方法合成非常有用。
合成的方法可能直到调用时才会作为独立的方法存在。
使用methodMissing合成方法
class Person {
def work() { "working..." }
def plays = ['Tennis', 'VolleyBall', 'BasketBall']
def methodMissing(String name, args) {
println "methodMissing called for $name"
def methodInList = plays.find { it == name.split('play')[1] }
if (methodInList) {
def impl = { Object[] vargs -> "playing ${name.split('play')[1]}..." }
Person instance = this
instance.metaClass."$name" = impl
impl(args)
} else {
throw new MissingMethodException(name, Person.class, args)
}
}
}
jack = new Person()
println jack.work()
println jack.playTennis()
println jack.playVolleyBall()
println jack.playBasketBall()
println jack.playTennis()
try {
jack.playPolitics()
} catch (Exception e) {
println "Error: " + e
}
对于实现了GroovyInterceptable的对象,调用该对象上的任何方法,都会调用到invokeMethod()。所以,又可以用以下方式:
class Person implements GroovyInterceptable {
def work() { "working..." }
def plays = ['Tennis', 'VolleyBall', 'BasketBall']
def invokeMethod(String name, args) {
println "intercepting call for $name"
def method = metaClass.getMetaMethod(name, args)
if (method) {
method.invoke(this, args)
} else {
metaClass.invokeMethod(this, name, args)
}
}
def methodMissing(String name, args) {
println "methodMissing called for $name"
def methodInList = plays.find { it == name.split('play')[1] }
if (methodInList) {
def impl = {
Object[] vargs -> "playing ${name.split('play')[1]}..."
}
Person instance = this
instance.metaClass."$name" = impl
impl(args)
} else {
throw new MissingMethodException(name, Person.class, args)
}
}
}
使用ExpandoMetaClass合成方法
在我们无法对源码进行修改的时候,我们也就无法使用methodMissing进行合成方法。这时,我们可以使用ExpandoMetaClass合成方法。
class Person {
def work() { "working..." }
}
Person.metaClass.methodMissing = { String name, args ->
def plays = ['Tennis', 'VolleyBall', 'BasketBall']
println "methodMissing called for $name"
def methodInList = plays.find { it == name.split('play')[1] }
if (methodInList) {
def impl = { Object[] vargs ->
"playing ${name.split('play')[1]}..."
}
Person.metaClass."$name" = impl
impl(args)
} else {
throw new MissingMethodException(name, Person.class, args)
}
}
jack = new Person()
println jack.work()
println jack.playTennis()
println jack.playTennis()
try {
jack.playPolitics()
} catch (Exception e) {
println "Error: " + e
}
为代码添加上invokeMethod拦截方法:
Person.metaClass.invokeMethod = { String name, args ->
println "intercepting call for ${name}"
def method = Person.metaClass.getMetaMethod(name,args)
if(method){
method.invoke(delegate,args)
}else{
Person.metaClass.invokeMissingMethod(delegate, name, args)
}
}
为具体的实例合成方法
class Person{}
def emc = new ExpandoMetaClass(Person)
emc.methodMissing = {String name,args ->
"I'm Jack of all trades... I can $name"
}
emc.initialize()
def jack = new Person()
def paul = new Person()
jack.metaClass = emc
println jack.sing()
println jack.dance()
println jack.juggle()
try{
paul.sing()
}catch (Exception e){
println e
}