简而言之,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
在Bar 中扩展Foo 与说是相同的(一直到它在C 中的实现)
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 也帮助我了解了他们之间的差异。