質問

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に含まれるモジュール

私の悪い英語でごめんなさい。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top