Rubyメタプログラミングに頭を悩ませています。 mixin/modulesは常に私を混乱させます。
それで、大きな違いはこれだけですか、それともより大きなドラゴンが潜んでいますか?.
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
があり、Mod
にKlazz
を含めると、Klazz
のインスタンスがMod
のメソッドにアクセスできます。または、Klazz
をMod
で拡張して、classKlazz
にMod
のメソッドへのアクセス権を与えることができます。ただし、o.extend Mod
を使用して任意のオブジェクトを拡張することもできます。この場合、Mod
と同じクラスを持つ他のすべてのオブジェクトはそうではありませんが、個々のオブジェクトはo
のメソッドを取得します。
extend-指定されたモジュールのメソッドと定数をターゲットのメタクラス(つまりシングルトンクラス)に追加します。
Klazz.extend(Mod)
を呼び出すと、KlazzにはModのメソッドが(クラスメソッドとして)ありますobj.extend(Mod)
を呼び出すと、objにはModのメソッドが(インスタンスメソッドとして)ありますが、obj.class
の他のインスタンスにはこれらのメソッドが追加されていません。extend
はパブリックメソッドですinclude-デフォルトでは、指定されたモジュールのメソッドをターゲットモジュール/クラスのインスタンスメソッドとしてミックスします。例えば.
class Klazz; include Mod; end;
を呼び出すと、Klazzのすべてのインスタンスが(インスタンスメソッドとして)Modのメソッドにアクセスできるようになります。include
は、コンテナクラス/モジュール内から呼び出されることを目的としているため、プライベートメソッドです。ただし、、モジュールは非常に頻繁にoverrideinclude
の動作をモンキーパッチでincluded
方法。これは、従来のRailsコードで非常に顕著です。 Yehuda Katzからの詳細 。
include
の詳細、およびデフォルトの動作(次のコードを実行したと仮定)
class Klazz
include Mod
end
@@foo
または@@bar
super
を呼び出すと、 Klazzの実際のスーパークラスのfooメソッドをチェックする前にMod#foo。詳細についてはRubySpecを参照してください。もちろん、 Ruby core documentation は常にこれらのことを行うのに最適な場所です。 RubySpecプロジェクト も機能を正確に文書化したため、素晴らしいリソースでした。
そのとおりです。
舞台裏では、includeは実際にはappend_featuresのエイリアスです(ドキュメントから):
Rubyのデフォルトの実装では、このモジュールがaModuleまたはその祖先のいずれかにまだ追加されていない場合、このモジュールの定数、メソッド、およびモジュール変数をaModuleに追加します。
RubySpecsを使用したDigのヒントなど、他のすべての回答は良好です。
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を実行すると、メソッドと定数はcopied。明らかに、注意を怠ると、定義を動的に複製することで多くのメモリを浪費する可能性があります。
ActiveSupport :: Concernを使用する場合、.included()機能により、インクルードクラスを直接書き換えることができます。 Concern内のClassMethodメソッドは、インクルードクラスに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'
にリンケージを追加します。
したがって、a
のメソッド検索パスは次のとおりです。a=> a '=> a'クラスにリンクされたモジュール=> A.
ルックアップパスを変更するprependメソッドもあります。
a => a '=> Aに追加されたモジュール=> A => Aに含まれたモジュール
私の悪い英語でごめんなさい。