Foo = Module.new
class MyClass
include Foo
end
当一个模块包含在一个类中时,会创建一个匿名代理类并将其设置为MyClass的超类。
MyClass.ancestors => [MyClass, Foo, ...]
但是当模块扩展时,内部会发生什么? Ruby如何处理这个问题?
答案 0 :(得分:5)
我认为你问的是Object#extend
因此,使用extend
,我可以将任何模块的方法包含在该对象中。
例如,我有一个名为HelperModule的模块:
module HelperModule
def foo
puts "from module helper"
end
end
obj = Object.new
obj.extend HelperModule
obj.foo # => "from module helper"
class MyClass
extend HelperModule
end
MyClass.foo # => "from module helper"
内部,根据Metaprogramming Ruby:
Object#extend()只是一个快捷方式,在接收者的本征类中包含一个模块。
ruby方法的简要说明叫:
obj
|
| class
| superclass superclass
---> ObjectClass --------------> SuperClass1 --------------> SuperClass2 ....
关于本征类和方法调用路径的详细说明,请参考这本很棒的书Metaprogramming Ruby
由于
答案 1 :(得分:0)
当一个模块在Ruby中扩展时,内部会发生什么?
当一个模块M
包含在类C
中时,会创建一个匿名代理类⟦M′⟧
(称为 include class ),以便其方法表指针指向M
的方法表。 (对于常量表和模块变量也是如此。)⟦M′⟧
的超类设置为C
的超类,C
的超类设置为⟦M′⟧
。
此外,如果M
包含其他模块,则会以递归方式应用该过程。
实际上,这只是默认行为。 真正发生的是include
调用M.append_features(C)
,您可以通过覆盖该方法来自定义所有这些行为。
我发现the source code of Module#append_features
in Rubinius非常易读。
答案 2 :(得分:0)
obj.extend SomeModule
与obj.eigenclass.include SomeModule
相同(注意:这只是伪代码,但您会明白这一点......)。
答案 3 :(得分:0)
简而言之,Ruby的include
方法将模拟继承:它将允许包含类访问包含的模块的实例方法,变量和常量,就好像它们已经在包含类本身中定义一样。 Ruby通过创建一个匿名代理类(称为eigenclass或singleton类)来实现这一点,如前所述,它引用包含的模块,并将其作为直接超类插入到包含类的祖先中。这就是Ruby中所谓的 mixin 。
但是,使用extend
可以与单例类进行更多的交互:
module Foo
def baz
'baz'
end
end
module Blah
def blah
'blah'
end
end
class Bar
extend Foo
include Blah
def self.magic
'magic'
end
end
在Foo
中扩展Bar
与在
Bar.singleton_class.send( :include, Foo )
这意味着它是Bar
的单例类,其中Foo
的方法和常量基本上是嵌入的,因此类Bar
是其单例类的一个实例,将继承这个新功能作为所谓的“类”方法。在Ruby中,模块和类只能有实例方法,因此创建“类”方法实际上只是在类的单例类中创建一个实例方法,以这种方式继承。
Bar.baz
# => 'baz'
Bar.magic
# => 'magic'
Bar.blah
# => NoMethodError
Bar.new.baz
# => NoMethodError
Bar.new.magic
# => NoMethodError
Bar.new.blah
# => 'blah'
您可以在此处查看include
和extend
的行为差异。要验证他们关于类祖先的行为,您可以要求Bar
及其单例类的祖先,它会向您显示模块Blah
是Bar
的直接父级,但是模块Foo
是单例类的直接父级。
Bar.ancestors
# => [Bar, Blah, Object, Kernel, BasicObject]
Bar.singleton_class.ancestors
# => [Foo, Class, Module, Object, Kernel, BasicObject]
从这些结果中,您可以看到包含模拟包含类中的继承,以及扩展如何仅包含在单例类中。
您可能会尝试查看this question一段时间后得到的答案;他们在解释include
和extend
功能的行为方面表现出色。
This article也帮助我了解他们之间的差异。