485 Stimmen

Was ist der Unterschied zwischen include und extend in Ruby?

Ich mache mich gerade mit der Ruby-Metaprogrammierung vertraut. Die Mixin/Module schaffen es immer, mich zu verwirren.

  • einschließen. : mischt sich in angegebene Modulmethoden als Beispielmethoden in der Zielklasse
  • erweitern. : mischt sich in angegebene Modulmethoden als Klassenmethoden in der Zielklasse

Liegt der Hauptunterschied also nur darin oder lauert ein größerer Drache? z.B..

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

402voto

John Douthat Punkte 39886

erweitern. - fügt die Methoden und Konstanten des angegebenen Moduls in die Metaklasse des Ziels (d. h. die Singleton-Klasse) ein z.B..

  • wenn Sie anrufen Klazz.extend(Mod) jetzt hat Klazz die Methoden von Mod (als Klassenmethoden)
  • wenn Sie anrufen obj.extend(Mod) hat obj nun die Methoden von Mod (als Instanzmethoden), aber keine andere Instanz von obj.class hat diese Methoden hinzugefügt.
  • extend ist eine öffentliche Methode

einschließen. - Standardmäßig mischt es die Methoden des angegebenen Moduls als Instanzmethoden in das Zielmodul/die Zielklasse ein. z.B.

  • wenn Sie anrufen class Klazz; include Mod; end; jetzt haben alle Instanzen von Klazz Zugriff auf die Methoden von Mod (als Instanzmethoden)
  • include ist eine private Methode, da sie aus der Containerklasse/dem Modul heraus aufgerufen werden soll.

Allerdings Module sehr oft Überschreiben Sie include Das Verhalten von monkey-Parcheando der included Methode. Dies ist in altem Rails-Code sehr auffällig. mehr Details von Yehuda Katz .

Weitere Informationen über include mit seinem Standardverhalten, vorausgesetzt, Sie haben den folgenden Code ausgeführt

class Klazz
  include Mod
end
  • Wenn Mod bereits in Klazz oder einem seiner Vorgänger enthalten ist, hat die Anweisung include keine Auswirkungen
  • Es schließt auch Mods Konstanten in Klazz ein, solange sie nicht miteinander kollidieren
  • Sie ermöglicht Klazz den Zugriff auf die Modulvariablen von Mod, z. B. @@foo o @@bar
  • löst ArgumentError aus, wenn es zyklische Includes gibt
  • Fügt das Modul als unmittelbaren Vorfahren des Aufrufers hinzu (d. h. es fügt Mod zu Klazz.ancestors hinzu, aber Mod wird nicht in die Kette von Klazz.superclass.superclass.superclass aufgenommen. Also, der Aufruf von super in Klazz#foo prüft auf Mod#foo, bevor es die foo-Methode der echten Oberklasse von Klazz überprüft. Siehe die RubySpec für Details).

Ja, natürlich, die Ruby-Kerndokumentation ist immer die beste Anlaufstelle für diese Dinge. Das RubySpec-Projekt war auch eine fantastische Ressource, weil sie die Funktionalität genau dokumentierte.

284voto

domgblackwell Punkte 4952

Was Sie gesagt haben, ist richtig. Aber es geht um mehr als das.

Wenn Sie eine Klasse haben Klazz und Modul Mod , einschließlich Mod en Klazz gibt Instanzen von Klazz Zugang zu Mod Methoden. Oder Sie können die Klazz con Mod die die Klasse Klazz Zugang zu Mod Methoden. Sie können aber auch ein beliebiges Objekt erweitern mit o.extend Mod . In diesem Fall wird das einzelne Objekt Mod Methoden, obwohl alle anderen Objekte mit der gleichen Klasse wie o nicht.

17voto

Toby Hede Punkte 36095

Das ist richtig.

Hinter den Kulissen ist include eigentlich ein Alias für Merkmale_anhängen die (aus den Dokumenten):

Die Standardimplementierung von Ruby ist die Konstanten, Methoden und Modulvariablen Variablen dieses Moduls zu aModule hinzu, wenn dieses Modul nicht bereits zu aModule oder einem seiner Vorgänger hinzugefügt wurde.

14voto

Chintan Punkte 343

Wenn Sie include eines Moduls in eine Klasse, werden die Modulmethoden importiert als Beispielmethoden .

Wenn Sie jedoch extend eines Moduls in eine Klasse, werden die Modulmethoden importiert als Klassenmethoden .

Wenn wir zum Beispiel ein Modul haben Module_test wie folgt definiert:

module Module_test
  def func
    puts "M - in module"
  end
end

Nun, für include Modul. Wenn wir die Klasse A wie folgt:

class A
  include Module_test
end

a = A.new
a.func

Die Ausgabe wird sein: M - in module .

Wenn wir die Zeile include Module_test con extend Module_test und den Code erneut ausführen, erhalten wir folgenden Fehler: undefined method 'func' for #<A:instance_num> (NoMethodError) .

Ändern des Methodenaufrufs a.func a A.func ändert sich die Ausgabe in: M - in module .

Aus der obigen Code-Ausführung geht hervor, dass, wenn wir include ein Modul, werden seine Methoden Beispielmethoden und wenn wir extend ein Modul, werden seine Methoden Klassenmethoden .

3voto

Ho-Sheng Hsiao Punkte 1275

Alle anderen Antworten sind gut, einschließlich des Tipps, RubySpecs zu durchsuchen:

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

Was die Anwendungsfälle betrifft:

Wenn Sie einschließen. Modul ReusableModule in der Klasse ClassThatIncludes werden die Methoden, Konstanten, Klassen, Submodule und andere Deklarationen referenziert.

Wenn Sie erweitern. Klasse ClassThatExtends mit Modul ReusableModule, dann werden die Methoden und Konstanten kopiert . Wenn Sie nicht aufpassen, können Sie durch die dynamische Duplizierung von Definitionen natürlich viel Speicherplatz verschwenden.

Wenn Sie ActiveSupport::Concern verwenden, können Sie mit der Funktion .included() die einschließende Klasse direkt umschreiben. erweitert (kopiert) in die einschließende Klasse.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X