如何在Ruby中动态改变继承
我想为Ruby中的类动态指定父类。考虑这个代码:如何在Ruby中动态改变继承,ruby,metaprogramming,Ruby,Metaprogramming,我想为Ruby中的类动态指定父类。考虑这个代码: class Agent def self.hook_up(calling_class, desired_parent_class) # Do some magic here end end class Parent def bar puts "bar" end end class Child def foo puts "foo" end Agent.hook_up(self, Parent
class Agent
def self.hook_up(calling_class, desired_parent_class)
# Do some magic here
end
end
class Parent
def bar
puts "bar"
end
end
class Child
def foo
puts "foo"
end
Agent.hook_up(self, Parent)
end
Child.new.bar
父类定义和子类定义都没有指定父类,因此它们都从对象继承。我的第一个问题是:我需要在Agent.hook\u
中做什么才能使Parent
成为Child
的超类(例如Child
对象可以继承'bar'方法)
我的第二个问题是:我是否需要将第一个参数传递给
Agent.hook\u-up
,或者hook\u-up
方法是否有某种方式可以通过编程方式确定调用它的类?也许您正在寻找这一点
Child = Class.new Parent do
def foo
"foo"
end
end
Child.ancestors # => [Child, Parent, Object, Kernel]
Child.new.bar # => "bar"
Child.new.foo # => "foo"
因为parent是Class.new的参数,所以可以将它与其他类交换
我以前在编写某些类型的测试时使用过这种技术。但是我很难想出很多好的借口来做这样的事情
我怀疑你真正想要的是一个模块
class Agent
def self.hook_up(calling_class, desired_parent_class)
calling_class.send :include , desired_parent_class
end
end
module Parent
def bar
"bar"
end
end
class Child
def foo
"foo"
end
Agent.hook_up(self, Parent)
end
Child.ancestors # => [Child, Parent, Object, Kernel]
Child.new.bar # => "bar"
Child.new.foo # => "foo"
不过,当然,根本不需要代理
module Parent
def bar
"bar"
end
end
class Child
def foo
"foo"
end
include Parent
end
Child.ancestors # => [Child, Parent, Object, Kernel]
Child.new.bar # => "bar"
Child.new.foo # => "foo"
Joshua已经给了您一个很好的备选方案列表,但是为了回答您的问题:在ruby中创建类之后,您不能更改类的超类。这根本不可能。正如前面指出的,您可能应该研究模块或动态创建类。但是,可以使用更改超类。甚至还有一个可用的。这只适用于核磁共振成像。应该很容易在Rubinius上构建(清除方法缓存将是主要问题),没有关于JRuby的线索。代码如下:
require 'evil'
class Agent
def self.hook_up(calling_class, desired_parent_class)
calling_class.superclass = desired_parent_class
end
end
class Parent
def bar
puts "bar"
end
end
class Child
def foo
puts "foo"
end
Agent.hook_up(self, Parent)
end
Child.new.bar
仅Ruby1.9:(1.8类似,但使用RCLASS(self)->super)
Ruby的类(在库中)可能会有所帮助,只要对象像基类一样嘎嘎作响,而不是实际上是基类的实例就足够了
require 'delegate'
class Agent < SimpleDelegator
def baz
puts "baz"
end
end
class BarParent
def bar
puts "bar"
end
end
class FooParent
def foo
puts "foo"
end
end
agent = Agent.new(FooParent.new)
agent.baz # => baz
agent.foo # => foo
agent.__setobj__(BarParent.new)
agent.baz # => baz
agent.bar # => bar
需要“委托”
类代理baz
agent.foo#=>foo
代理。\uuuu setobj\uuuuu(BarParent.new)
agent.baz#=>baz
agent.bar#=>bar
看看这个
class MyClass < inherit_orm("Adapter")
end
因此,当instanceMyClass
时,它将根据orm
和model
从动态类继承。
请确保在模块中同时定义这两个选项。它在gem()中运行良好
我希望能帮上忙。。再见 我知道这个问题很老了,已经有了一些很好的答案。然而,我仍然错过了某种解决方案 如果您的目的不是动态分配超类,而是创建一个钩子来执行继承()上的一些代码。有一种内置的方法可以做到这一点 继承(子类) 每当创建当前类的子类时调用回调 例如:
class Foo
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
end
class Bar < Foo
end
class Baz < Bar
end
见:
如果您打算动态创建类,我建议您查看。Um,如果您要动态设置或更改类的父级,那么可以合理地断言您的对象模型没有反映真正的is-a关系。一个更合适的解决方案可能是组合。你应该弄清楚这是否是一个思维练习,或者为什么你想在实践中使用它。动态反射语言并不意味着动态更改继承是干净的。伟大的(表达)力量带来了巨大的责任:)@cletus我一般不太喜欢继承,但对于我目前所关注的问题,继承和is-a关系完美地描述了这种关系。唯一的问题是,一个对象在运行时之前不知道它是什么。我猜在生产代码中使用“邪恶”可能会受到反对:-)动态创建一个类可能是我最终使用的解决方案。谢谢谢谢你,约书亚。由于我似乎无法在运行时更改继承链,因此我可能会在解决方案中使用您的第一个选项的一些变体。我很感激你花了这么多时间来回应。如果这真的是你想做的,你可以看看Ryan Davis写的《改变课堂》。这是他在5:00介绍代码的视频,这是该项目的主页。注意免责声明“是的,这很危险……如果您的计算机点火,请不要向我哭诉。”@Borromakot挑战状态?@Borromakot,除非您覆盖了它们的内存位置:)
def inherit_orm(model="Activity", orm=nil)
orm = Config.orm || orm
require "orm/#{orm.to_s}"
"ORM::#{orm.to_s.classify}::#{model}".constantize
end
class Foo
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
end
class Bar < Foo
end
class Baz < Bar
end
New subclass: Bar
New subclass: Baz