Ruby の include と extend の違いは何ですか?
質問
Ruby のメタプログラミングについて少し理解しているところです。ミックスインやモジュールはいつも私を混乱させます。
- 含む:指定されたモジュールメソッドを次のようにミックスします インスタンスメソッド 対象クラスで
- 伸ばす:指定されたモジュールメソッドを次のようにミックスします クラスメソッド 対象クラスで
では、大きな違いはこれだけなのでしょうか、それともさらに大きなドラゴンが潜んでいるのでしょうか?例えば
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!"
解決
あなたが言ったことは正しいです。ただし、それだけではありません。
クラス Klazz
およびモジュール Mod
がある場合、 Klazz
の Mod
を含めると、 Klazz
Mod
のメソッドへのアクセス。または、 Mod
で Klazz
を拡張して、 class Klazz
に Mod
へのアクセス権を与えることができます。 sメソッド。しかし、 o.extend Mod
で任意のオブジェクトを拡張することもできます。この場合、 o
と同じクラスを持つ他のすべてのオブジェクトはそうではありませんが、個々のオブジェクトは Mod
のメソッドを取得します。
他のヒント
伸ばす - 指定されたモジュールのメソッドと定数をターゲットのメタクラスに追加します(つまり、シングルトンクラス)例
- 電話したら
Klazz.extend(Mod)
, 、Klazz には Mod のメソッドが (クラス メソッドとして) 追加されました。 - 電話したら
obj.extend(Mod)
, 、今、obj には Mod のメソッド (インスタンス メソッドとして) がありますが、他のインスタンスはありません。obj.class
それらのメソッドが追加されています。 extend
パブリックメソッドです
含む - デフォルトでは、指定されたモジュールのメソッドがターゲット モジュール/クラスのインスタンス メソッドとして混合されます。例えば
- 電話したら
class Klazz; include Mod; end;
, 、Klazz のすべてのインスタンスが Mod のメソッド (インスタンス メソッドとして) にアクセスできるようになりました。 include
コンテナ クラス/モジュール内から呼び出されるように意図されているため、プライベート メソッドです。
しかし, 、モジュールは非常に頻繁に オーバーライド include
モンキーパッチによる の動作 included
方法。これは従来の Rails コードで非常に顕著です。 Yehuda Katz からの詳細.
詳細については、 include
, 、デフォルトの動作で、次のコードを実行したと仮定します。
class Klazz
include Mod
end
- Mod が既に Klazz またはその先祖の 1 つに含まれている場合、include ステートメントは効果がありません。
- 衝突しない限り、Klazz の Mod 定数も含まれます。
- これにより、Klazz が Mod のモジュール変数にアクセスできるようになります。
@@foo
または@@bar
- 循環インクルードがある場合は ArgumentError が発生します
- モジュールを呼び出し元の直接の祖先としてアタッチします (つまり、Klazz.ancestors に Mod を追加しますが、Klazz.superclass.superclass.superclass のチェーンには Mod は追加されません。それで、電話をかけると、
super
Klazz#foo では、Klazz の実際のスーパークラスの foo メソッドをチェックする前に、Mod#foo をチェックします。詳細については、RubySpec を参照してください。)。
もちろん、 Rubyコアのドキュメント これらのことを行うには、常に最適な場所です。 RubySpec プロジェクト また、機能が正確に文書化されているため、素晴らしいリソースでした。
それは正しい。
シーンの背後では、includeは実際には append_features のエイリアスであり、(ドキュメントから):
Rubyのデフォルトの実装は 定数、メソッド、およびモジュールを追加します このモジュールの変数をaModuleに このモジュールはまだ追加されていません aModuleまたはその祖先の1つ。
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を include すると、メソッド、定数、クラス、サブモジュール、およびその他の宣言が参照されます。
モジュールReusableModuleでClassThatExtendsクラスを extend すると、メソッドと定数がコピーされます。明らかに、注意を怠ると、定義を動的に複製することで多くのメモリを浪費する可能性があります。
ActiveSupport :: Concernを使用する場合、.included()機能を使用すると、インクルードクラスを直接書き換えることができます。 Concern内のClassMethodsモジュールは、インクルードクラスに extended (コピー)されます。
また、動作するメカニズムを説明したいと思います。正しくない場合は修正してください。
include
を使用すると、クラスからいくつかのメソッドを含むモジュールへのリンクが追加されます。
class A
include MyMOd
end
a = A.new
a.some_method
オブジェクトにはメソッドがありません。クラスとモジュールのみがあります。
したがって、 a
がメッセージ some_method
を受信すると、 a
の固有クラスで検索メソッド some_method
を開始し、次に > A
クラス、および A
クラスモジュールにリンクされている場合(逆の順序で、最後に含まれたものが優先)。
extend
を使用すると、オブジェクトの固有クラスのモジュールにリンケージが追加されます。
したがって、A.new.extend(MyMod)を使用する場合、モジュールのリンクをAのインスタンス固有クラスまたは a '
クラスに追加します。
そして、A.extend(MyMod)を使用する場合、リンケージをA(オブジェクトの、クラスもオブジェクト)eigenclass A '
に追加します。
so a
のメソッド検索パスは次のとおりです。
=> a '=>モジュールをgtにリンク; A。
検索パスを変更するprependメソッドもあります:
a => a '=> A =&gtに追加されたモジュール。 => Aに含まれるモジュール
私の悪い英語でごめんなさい。