Foo = Module.new
class MyClass
include Foo
end
当一个模块包含在一个类中时,会创建一个匿名代理类并将其设置为 MyClass 的超类。
MyClass.ancestors => [MyClass, Foo, ...]
但是当一个模块被扩展时,内部会发生什么呢?Ruby 是如何处理这个问题的?
Foo = Module.new
class MyClass
include Foo
end
当一个模块包含在一个类中时,会创建一个匿名代理类并将其设置为 MyClass 的超类。
MyClass.ancestors => [MyClass, Foo, ...]
但是当一个模块被扩展时,内部会发生什么呢?Ruby 是如何处理这个问题的?
我想你问的是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"
在内部,根据元编程 Ruby:
Object#extend() 只是一个快捷方式,它在接收器的特征类中包含一个模块。
ruby 方法调用的简要说明:
obj
|
| class
| superclass superclass
---> ObjectClass --------------> SuperClass1 --------------> SuperClass2 ....
关于eigenclass和方法调用路径的详细解释,请参考这本很棒的书Metaprogramming Ruby
谢谢
当一个模块在 Ruby 中扩展时,内部会发生什么?
当一个模块M
包含在一个类C
中时,会创建一个匿名代理类⟦M′⟧
(称为包含类),使其方法表指针指向M
的方法表。(对于常量表和模块变量也是如此。)⟦M′⟧
的超类设置为C
的超类,C
的超类设置为⟦M′⟧
。
此外,如果M
包含其他模块,则递归应用该过程。
实际上,这只是默认行为。真正发生的是include
调用M.append_features(C)
,您可以通过覆盖该方法来自定义所有这些行为。
obj.extend SomeModule
与obj.eigenclass.include SomeModule
(注意:这只是伪代码,但你会明白的......)。
简而言之,Ruby 的include
方法将模拟继承:它将允许包含类访问被包含模块的实例方法、变量和常量,就好像它们已在包含类本身中定义一样。正如您所提到的,Ruby 通过创建一个匿名代理类(称为 eigenclass 或单例类)来做到这一点,它引用包含的模块,并将其作为直接超类插入到包含类的祖先中。这就是 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
说是相同的(一直到它在 C 中的实现)
Bar.singleton_class.send( :include, Foo )
这意味着它是Bar
其中的方法和常量Foo
本质上嵌入的单例类,因此Bar
作为其单例类的实例的 class 将继承这个新功能作为所谓的“类”方法。在 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]
从这些结果中,您可以看到包含如何模拟包含类中的继承,以及扩展如何仅包含在单例类中。
您可以尝试查看我不久前为这个问题得到的答案;include
他们在解释和extend
功能
的行为方面做得非常出色。这篇文章也帮助我理解了他们的不同之处。