2013-07-09 166 views
14

我一直在閱讀一些關於Ruby的mixin方法的文章,extendinclude,我對這種行爲還不太確定。我知道extend會將給定模塊的實例方法作爲單例方法添加到執行擴展的模塊中,並且include將基本上將一個模塊(方法,常量,變量)的內容附加到正在執行包含的模塊的內容上,從而有效地定義他們在接收器中。Ruby mixins:擴展幷包含

但是,經過一些修改,試圖感受行爲將如何體現,我有幾個問題。這裏是我的測試設置:

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。爲什麼會發生?

+0

+1這個問題的偉大格式。 – sargas

回答

24

區別在於include會將包含的類添加到包含類的祖先中,而extend會將擴展類添加到擴展類的單例類的祖先中。唷。讓我們先來觀察會發生什麼:

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。發生的另一件事是,一個類的所有祖先都成爲實例創建時的單例類的祖先。所以這將評估爲真正的所有新創建的任何類的實例:

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的簡寫。

0
Egg.is_a? Egg # => false 

include(有效)改變了Egg類的實例。雖然這是不太一樣的,這是非常相似,做這樣的事情

class Egg < Bar 
end 

當延長將增加類的方法,所以這是非常相似,做這樣的事情

class Bacon 
    class << self 
    include Bar 
    end 
end 

你可以認爲它像包含類的變化實例,其中extend實際上改變了類。

+0

也許你錯誤地鍵入了一些東西,但是當我做了'Egg.new.is_a?蛋'它返回'true'。你的意思是'Egg.is_a?蛋#=>假'? – DesAdams

+0

是的,我做到了。我會編輯它。 – Olives