457

Ruby のメタプログラミングについて頭を悩ませているところです。ミックスイン/モジュールは常に私を混乱させます。

  • include : 指定されたモジュール メソッドをターゲット クラスのインスタンス メソッドとして混合します。
  • extend : 指定されたモジュール メソッドをターゲット クラスのクラスメソッドとして混合します。

大きな違いはこれだけなのか、それとももっと大きなドラゴンが潜んでいるのか? 例えば

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!"
4

7 に答える 7

368

拡張- 指定されたモジュールのメソッドと定数をターゲットのメタクラス (つまり、シングルトン クラス) に追加します。

  • を呼び出すとKlazz.extend(Mod)、Klazz は Mod のメソッドを (クラス メソッドとして) 持っています。
  • を呼び出すとobj.extend(Mod)、obj には Mod のメソッド (インスタンス メソッドとして) が含まれますが、obj.classこれらのメソッドが追加された の他のインスタンスはありません。
  • extendパブリックメソッドです

include - デフォルトでは、指定されたモジュールのメソッドをターゲット モジュール/クラスのインスタンス メソッドとして混合します。例えば

  • を呼び出すとclass Klazz; include Mod; end;、Klazz のすべてのインスタンスが Mod のメソッドに (インスタンス メソッドとして) アクセスできるようになります。
  • includeコンテナクラス/モジュール内から呼び出されることを意図しているため、プライベートメソッドです。

ただし、モジュールは、メソッドにモンキー パッチを適用することによって、 の動作をオーバーライド することがよくあります。これは、従来の Rails コードでは非常に顕著です。詳細は Yehuda Katz からincludeincluded

include次のコードを実行したと仮定した場合の、デフォルトの動作に関する の詳細

class Klazz
  include Mod
end
  • Mod がすでに Klazz またはその祖先の 1 つに含まれている場合、include ステートメントは効果がありません
  • 衝突しない限り、Klazz の Mod の定数も含まれます。
  • これにより、Klazz は Mod のモジュール変数にアクセスできます@@foo@@bar
  • 循環インクルードがある場合は ArgumentError を発生させます
  • モジュールを呼び出し元の直接の祖先としてアタッチします (つまり、Mod を Klazz.ancestors に追加しますが、Mod は Klazz.superclass.superclass.superclass のチェーンには追加されません。したがって、superKlazz#foo を呼び出すと、チェックの前に Mod#foo がチェックされます。 Klazz の実際のスーパークラスの foo メソッドに (詳細については RubySpec を参照してください)。

もちろん、Ruby コア ドキュメントは常に、これらのことを行うのに最適な場所です。RubySpec プロジェクトも素晴らしいリソースでした。機能を正確に文書化したからです。

于 2011-02-15T19:10:25.460 に答える
273

あなたが言ったことは正しいです。しかし、それだけではありません。

クラスKlazzとモジュールがある場合はMod、を含めModて、のメソッドへのアクセスのKlazzインスタンスを提供します。または、クラスにのメソッドへのアクセスを許可して拡張することもできます。ただし、。を使用して任意のオブジェクトを拡張することもできます。この場合、同じクラスを持つ他のすべてのオブジェクトが取得しない場合でも、個々のオブジェクトはのメソッドを取得します。KlazzModKlazzMod KlazzModo.extend ModModo

于 2008-10-01T09:59:38.203 に答える
17

そのとおりです。

舞台裏では、 include は実際にはappend_featuresのエイリアスであり、(ドキュメントから):

Ruby のデフォルトの実装では、このモジュールが aModule またはその祖先のいずれかにまだ追加されていない場合、このモジュールの定数、メソッド、およびモジュール変数を aModule に追加します。

于 2008-10-01T08:08:30.777 に答える
3

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を含めると、メソッド、定数、クラス、サブモジュール、およびその他の宣言が参照されます。

クラスClassThatExtendsをモジュールReusableModuleで拡張すると、メソッドと定数がコピーされます。明らかに、注意しないと、定義を動的に複製することで多くのメモリを浪費する可能性があります。

ActiveSupport :: Concernを使用する場合、.included()機能を使用すると、インクルードクラスを直接書き換えることができます。コンサーン内のモジュールClassMethodsは、インクルードクラスに拡張(コピー)されます。

于 2011-09-22T21:02:06.940 に答える
0

、およびクラス内で使用されるメソッドを比較する非常に役立つ記事に出くわしました。includeextendprepend

includeは、モジュール メソッドをインスタンス メソッドとしてクラスにextend追加しますが、モジュール メソッドをクラス メソッドとして追加します。含まれるまたは拡張されるモジュールは、それに応じて定義する必要があります

于 2021-11-26T12:25:58.633 に答える