我已经读了一些关于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
如我所料,module
Bar
获得Baz
(#blorg
)中定义的实例方法,就好像它们是由include方法本身定义的一样,而classBacon
通过扩展获得singleton方法Bacon::blah
和Bacon::blorg
。Bacon.blah # => blah
Bacon.blorg # => blorg
类获得在
Egg
中定义的方法(Bar
现在是#blah
)作为实例方法。Egg.new.blah # => blah
Egg.new.blorg # => blorg
我明白了,所以这很好。
但是,我不理解使用
#blorg
和#ancestors
方法得到的响应。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?
方法返回#is_a?
,但它不会添加到类的祖先,反之亦然:类的祖先包含要包含的模块,但是当查询时,true
方法返回#is_a?
。为什么会这样? 最佳答案
不同之处在于include
将把included类添加到included类的祖先,而extend
将把扩展类添加到扩展类的singleton类的祖先。呸。让我们先观察一下发生了什么:
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
。另一件事是,当实例被创建时,类的所有祖先都成为实例的singleton类的祖先。因此,对于任何类的所有新创建实例,此值都将计算为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
只是到达singleton类的奇怪语法。所以extend
实际上只是在singleton类中include
的缩写。