我已经读了一些关于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

如我所料,moduleBar获得Baz#blorg)中定义的实例方法,就好像它们是由include方法本身定义的一样,而classBacon通过扩展获得singleton方法Bacon::blahBacon::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

那么这一切意味着什么?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只是到达singleton类的奇怪语法。所以extend实际上只是在singleton类中include的缩写。

09-10 05:20
查看更多