Ruby mixins:扩展和包含
Posted
技术标签:
【中文标题】Ruby mixins:扩展和包含【英文标题】:Ruby mixins: extend and include 【发布时间】:2013-07-07 08:04:34 【问题描述】:我一直在阅读一些关于 Ruby 的 mixin 方法的文章,extend
和 include
,但我仍然不太确定这种行为。我知道extend
会将给定模块的实例方法作为单例方法添加到进行扩展的模块中,并且include
本质上会将模块的内容(方法、常量、变量)附加到执行扩展的模块中包括,在接收器中有效地定义它们。
但是,经过一些修改,试图了解行为将如何表现,我有几个问题。这是我的测试设置:
module Baz
def blorg
puts 'blorg'
end
end
module Bar
include Baz
def blah
puts 'blah'
end
end
module Foo
extend Bar
end
class Bacon
extend Bar
end
class Egg
include Bar
end
正如我所料,模块Bar
获得了在Baz
(#blorg
) 中定义的实例方法,就好像它们是由于包含方法而在其自身中定义的一样,而类Bacon
获得了单例方法 Bacon::blah
和 Bacon::blorg
的扩展。
Bacon.blah # => blah
Bacon.blorg # => blorg
而Egg
类获得Bar
(#blah
和现在的#blorg
)中定义的方法作为实例方法。
Egg.new.blah # => blah
Egg.new.blorg # => blorg
我明白了,这很好。
但是,我不明白使用 #ancestors
和 #is_a?
方法得到的响应。
Bacon.ancestors # => [Bacon, Object, Kernel, BasicObject]
Bacon.is_a? Bar # => true
Egg.ancestors # => [Egg, Bar, Baz, Object, Kernel, BasicObject]
Egg.is_a? Bar # => false
似乎扩展一个模块会导致#is_a?
方法在查询该模块时返回true
,但它没有添加到类的祖先中,反之亦然:该类包含被包含的模块,但#is_a?
方法在查询时返回false
。为什么会这样?
【问题讨论】:
+1 这个问题的格式很好。 【参考方案1】:不同之处在于include
会将包含类添加到包含类的祖先,而extend
会将扩展类添加到扩展类的单例类的祖先。唷。让我们先观察会发生什么:
Bacon.ancestors
#=> [Bacon, Object, Kernel, BasicObject]
Bacon.singleton_class.ancestors
#=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject]
Bacon.new.singleton_class.ancestors
#=> [Bacon, Object, Kernel, BasicObject]
Bacon.is_a? Bar
#=> true
Bacon.new.is_a? Bar
#=> false
对于Egg
类
Egg.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]
Egg.singleton_class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]
Egg.new.singleton_class.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]
Egg.is_a? Bar
#=> false
Egg.new.is_a? Bar
#=> true
所以foo.is_a? Klass
实际上做的是检查foo.singleton_class.ancestors
是否包含Klass
。发生的另一件事是,当创建实例时,类的所有祖先都成为实例的单例类的祖先。因此,对于任何类的所有新创建的实例,这将评估为 true:
Egg.ancestors == Egg.new.singleton_class.ancestors
那么这一切意味着什么? extend
和 include
在不同的层次上做同样的事情,我希望下面的例子能清楚地说明这一点,因为扩展类的两种方式本质上是等价的:
module A
def foobar
puts 'foobar'
end
end
class B
extend A
end
class C
class << self
include A
end
end
B.singleton_class.ancestors == C.singleton_class.ancestors
#=> true
其中class << self
只是进入单例类的奇怪语法。所以extend
只是单例类中include
的简写。
【讨论】:
【参考方案2】:Egg.is_a? Egg # => false
包含(有效地)更改Egg
类的实例。虽然不太一样,但和做类似的事情很相似
class Egg < Bar
end
extend 的时候会添加类方法,所以这和做类似
class Bacon
class << self
include Bar
end
end
你可以把它想象成包含更改类的实例,而扩展实际上更改了类。
【讨论】:
也许你打错了东西,但是当我输入Egg.new.is_a? Egg
时,它返回true
。你的意思是Egg.is_a? Egg # => false
?以上是关于Ruby mixins:扩展和包含的主要内容,如果未能解决你的问题,请参考以下文章