Ruby中包含和擴展有什麼區別?

剛剛開始關注Ruby元編程。 mixin / modules老是讓我困惑。 html

  • include :將指定模塊方法中的混合做爲目標類中的實例方法
  • extend :將指定的模塊方法混合爲目標類中的類方法

那麼主要區別在於這仍是潛伏着更大的龍? 例如 git

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

#1樓

那是對的。 github

在幕後,include其實是append_features的別名,(來自文檔): 編程

Ruby的默認實現是將此模塊的常量,方法和模塊變量添加到aModule(若是此模塊還沒有添加到aModule或其祖先之一)。 ruby


#2樓

你所說的是對的。 然而,除此以外還有更多。 app

若是你有一個類Klazz和模塊Mod ,包括Klazz ModKlazzKlazz訪問Mod的方法。 或者您可使用Mod擴展Klazz ,使Klazz Klazz訪問Mod的方法。 可是你也能夠用o.extend Mod擴展一個任意對象。 在這種狀況下,單個對象獲取Mod的方法,即便與o具備相同類的全部其餘對象也沒有。 spa


#3樓

我還想解釋它的做用機制。 若是我不對,請糾正。 .net

當咱們使用include咱們將從類中添加一個連接到一個包含一些方法的模塊。 code

class A
include MyMOd
end

a = A.new
a.some_method

對象沒有方法,只有clases和模塊。 因此當a接收消息some_method它開始在a特徵類中搜索方法some_method ,而後在A類中而後連接到A類模塊,若是有一些(以相反順序,最後包括勝利)。 htm

當咱們使用extend咱們將對象的特徵類中的模塊添加連接。 所以,若是咱們使用A.new.extend(MyMod),咱們將模塊的連接添加到A的實例特徵類或a'類。 若是咱們使用A.extend(MyMod),咱們將添加連接到A(對象,類也是對象)本徵類A'

所以對於方法查找路徑a是以下:a =>一個「=>連接的模塊爲」類=> A.

還有一個更改查找路徑的prepend方法:

a => a'=>前置模塊A => A =>包含模塊到A

對不起,個人英語很差。


#4樓

extend - 將指定模塊的方法和常量添加到目標的元類(即單例類),例如

  • 若是你調用Klazz.extend(Mod) ,如今Klazz有Mod的方法(做爲類方法)
  • 若是你調用obj.extend(Mod) ,如今obj有Mod的方法(做爲實例方法),但沒有其餘obj.class實例添加了這些方法。
  • extend是一種公共方法

include - 默認狀況下,它將指定模塊的方法混合爲目標模塊/類中的實例方法。 例如

  • 若是你打電話給class Klazz; include Mod; end; class Klazz; include Mod; end; ,如今Klazz的全部實例均可以訪問Mod的方法(做爲實例方法)
  • include是一個私有方法,由於它是從容器類/模塊中調用的。

可是 ,模塊常常經過猴子修補included方法來覆蓋 include的行爲。 這在傳統的Rails代碼中很是突出。 來自Yehuda Katz的更多細節

假設您運行如下代碼,有關include其默認行爲的更多詳細信息

class Klazz
  include Mod
end
  • 若是Mod已包含在Klazz或其祖先之一中,則include語句無效
  • 它還包括Mod在Klazz中的常量,只要它們不衝突
  • 它使Klazz能夠訪問Mod的模塊變量,例如@@foo@@bar
  • 若是存在循環包含則引起ArgumentError
  • 將模塊做爲調用者的直接祖先附加(即它將Mod添加到Klazz.ancestors,可是Mod沒有添加到Klazz.superclass.superclass.superclass的鏈中。因此,在Klazz #foo中調用super將檢查Mod#foo以前檢查Klazz真正的超類的foo方法。有關詳細信息,請參閱RubySpec。)。

固然, ruby核心文檔老是最適合這些事情的地方。 RubySpec項目也是一個很棒的資源,由於他們精確地記錄了這些功能。


#5樓

全部其餘答案都很好,包括挖掘RubySpecs的提示:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

至於用例:

若是在ClassThatIncludes類中包含模塊ReusableModule,則會引用方法,常量,類,子模塊和其餘聲明。

若是使用模塊ReusableModule 擴展類ClassThatExtends,則會複製方法和常量。 顯然,若是你不當心,你能夠經過動態複製定義來浪費大量內存。

若是使用ActiveSupport :: Concern,則.included()功能容許您直接重寫包含類。 關注內的模塊ClassMethods被擴展 (複製)到包含類中。

相關文章
相關標籤/搜索