Ruby mixins:扩展和包含

时间:2013-07-09 16:01:43

标签: ruby inheritance include extend

我一直在阅读一些关于Ruby的mixin方法的文章,extendinclude,我仍然不太确定这种行为。我理解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::blahBacon::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。为什么会这样?

2 个答案:

答案 0 :(得分:25)

不同之处在于include会将包含的类添加到包含类的祖先,而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。另一件事是,在创建实例时,类的所有祖先都成为实例单例类的祖先。因此,对于所有新创建的任何类的实例,这将评估为true:

Egg.ancestors == Egg.new.singleton_class.ancestors

那么这一切意味着什么呢? extendinclude在不同的级别上执行相同的操作,我希望下面的示例清楚地说明,因为扩展类的两种方法基本上是等价的:

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的简写。

答案 1 :(得分:0)

Egg.is_a? Egg # => false

包含(有效)更改Egg类的实例。虽然它不完全相同,但它与做

这样的事情非常相似
class Egg < Bar
end

当extend将添加类方法时,这与执行类似

的操作非常类似
class Bacon
  class << self
    include Bar
  end
end

您可以将其视为包括类的更改实例,其中extend实际上更改了类。