Моя цель - изменить порядок делегирования методов в 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
динамически добавлять методы во время выполнения.
The специфицировано c желаемое поведение заключается в том, что любой метод, вызываемый Weapon
, сначала будет искать его в prefix
(если он не равен нулю). Если его там нет, Weapon
будет искать метод, используя его обычное поведение (как показано здесь: https://docs.groovy-lang.org/latest/html/documentation/core-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
в Weapon, но 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
или без него) вместо этого возвращал ноль. Я сделал ошибку в своей реализации, есть ли лучший способ сделать это, или это даже невозможно?