Ruby mixins:扩展和包含

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::blahBacon::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.