Des*_*ams 15 ruby inheritance include extend
我一直在阅读有关Ruby的混入方法几篇文章,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
Run Code Online (Sandbox Code Playgroud)
正如我所期望的那样,模块Bar
获得了Baz
(#blorg
)中定义的实例方法,好像它们由于包含方法而被自己定义,并且类Bacon
获得了单例方法Bacon::blah
和Bacon::blorg
扩展.
Bacon.blah # => blah
Bacon.blorg # => blorg
Run Code Online (Sandbox Code Playgroud)
并且类Egg
获得Bar
(#blah
和现在#blorg
)定义的方法作为实例方法.
Egg.new.blah # => blah
Egg.new.blorg # => blorg
Run Code Online (Sandbox Code Playgroud)
我得到了所有这些,所以这很好.
但是,我不理解使用#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
Run Code Online (Sandbox Code Playgroud)
似乎扩展模块会导致#is_a?
方法true
在查询该模块时返回,但是它没有添加到类的祖先,反之亦然,包含:类的祖先包含所包含的模块,但是查询时#is_a?
返回该方法false
.为什么会这样?
Pat*_*ity 26
The difference is that include
will add the included class to the ancestors of the including class, whereas extend
will add the extended class to the ancestors of the extending classes' singleton class. Phew. Let's first observe what happens:
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
Run Code Online (Sandbox Code Playgroud)
And for the Egg
class
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
Run Code Online (Sandbox Code Playgroud)
So what foo.is_a? Klass
actually does is to check whether foo.singleton_class.ancestors
contains Klass
. The other thing happening is that all the ancestors of a class become ancestors of an instances' singleton class when the instance is created. So this will evaluate to true for all newly created instances of any class:
Egg.ancestors == Egg.new.singleton_class.ancestors
Run Code Online (Sandbox Code Playgroud)
So what does all this mean? extend
and include
do the same thing on different levels, i hope the following example makes this clear as both ways to extend a class are essentially equivalent:
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
Run Code Online (Sandbox Code Playgroud)
where class << self
is just the odd syntax to get to the singleton class. So extend
really just is a shorthand for include
in the singleton class.