如何改变Groovy中方法授权的顺序?

问题描述 投票:0回答:1

我的目标是改变Groovy中方法授权的顺序,以便能够动态地覆盖Groovy对象中的方法。

我有一个名为 Weapon 定义如下。

class Weapon {
    Prefix prefix

    String method() {
        'Called from Weapon'
    }
}

其中 prefix 是这个类的一个实例。

class Prefix {
    final Map<String, Closure> methods = [:]

    void propertyMissing(String name, Closure value) {
        if (value != null) {
            methods[name] = value
        }
        else {
            methods.remove(name)
        }
    }

    def methodMissing(String name, args) {
        if (!methods.containsKey(name)) {
            throw new MissingMethodException(name, Prefix, args)
        }
        methods.(name)(args)
    }
}

这种设计允许 Prefix 以在运行时动态添加方法。

在运行时动态添加方法。特定 想要的行为是,任何由 Weapon 将首先在 prefix (如果它不是空的)。如果它不存在,那么 Weapon 将使用其正常行为搜索该方法(如这里所示。https:/docs.groovy-lang.orglatesthtmldocumentationcore-metaprogramming.html#_runtime_metaprogramming。).

下面是一个简单的例子,说明所需的行为。

Weapon w = new Weapon()
Prefix p = new Prefix()

p.method = { 'Called from Prefix' }
w.prefix = p

assert w.method() == 'Called from Prefix'

w.prefix = null

assert w.method() == 'Called from Weapon'

我对Groovy的元编程相当陌生,所以我并不了解它的所有功能。起初,我以为可以通过简单地覆盖掉 invokeMethod 中的方法,但Groovy最终还是在找到这个方法之前,找到了 invokeMethod 被调用过(我假设它是在元类或类中找到的)。

我能想到的唯一的解决方案是创建一个自定义的元类,用于 Weapon 这将首先检查 Weapon的前缀。我的实现在下面。

class WeaponMetaClass {
    WeaponMetaClass(MetaClass metaClass) {
        super(metaClass)
    }
    Object invokeMethod(Object object, String methodName, Object[] args) {
        if (object instanceof Weapon && object.prefix != null) {
            try {
                return object.prefix.(methodName)(args)
            }
            catch (MissingMethodException ignored) {
            }
        }
        return super.invokeMethod(object, methodName, args)
    }
}

然而,这并不奏效,每一个方法的调用都是来自于 Weapon (有或没有 prefix)返回的是null。我的实现是否出错了,有没有更好的方法,或者根本不可能做到这一点?

groovy metaprogramming
1个回答
1
投票

你可以使用默认的metaClass来实现所需的行为。

class Weapon {
    void setPrefix(Map m){
        this.metaClass=null                     //reset to default class behavior
        if(m)m.each{k,v-> this.metaClass[k]=v}  //redefine methods/properties from map
    }

    String method() {
        'Called from Weapon'
    }
}

def w = new Weapon()
//you could wrap prefix with custom class - however it's just a map container
def prefix=[
    method: {'Called from Prefix'}
]


assert w.method()=='Called from Weapon'

w.prefix = prefix
assert w.method()=='Called from Prefix'

w.prefix = null
assert w.method()=='Called from Weapon'
© www.soinside.com 2019 - 2024. All rights reserved.