Was ist der Unterschied zwischen Einschließen und Erweitern in Ruby?

414

Ich beschäftige mich nur mit Ruby-Metaprogrammierung. Das Mixin / die Module schaffen es immer, mich zu verwirren.

  • include : mischt bestimmte Modulmethoden als Instanzmethoden in der Zielklasse ein
  • verlängern : mischt angegebene Modulmethoden als Klassenmethoden in der Zielklasse ein

Ist der Hauptunterschied genau das 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!"
Gishu
quelle
Überprüfen Sie auch diesen Link heraus: juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby
Donato

Antworten:

249

Was Sie gesagt haben, ist richtig. Es steckt jedoch noch mehr dahinter.

Wenn Sie eine Klasse haben Klazzund Modul Mod, einschließlich Modin Klazzgibt Instanzen KlazzZugriff auf Mod‚s Methoden. Oder Sie können erweitern Klazzmit ModAngabe der Klasse Klazz Zugriff auf Mod‚s Methoden. Sie können aber auch ein beliebiges Objekt mit erweitern o.extend Mod. In diesem Fall erhält das einzelne Objekt Moddie Methoden, obwohl alle anderen Objekte dieselbe Klasse haben wie onicht.

domgblackwell
quelle
324

verlängern - Fügt die Methoden und Konstanten des angegebenen Moduls zur Metaklasse des Ziels hinzu (dh zur Singleton-Klasse), z

  • Wenn Sie aufrufen Klazz.extend(Mod), hat Klazz jetzt Mods Methoden (als Klassenmethoden)
  • Wenn Sie aufrufen obj.extend(Mod), hat obj jetzt Mods Methoden (als Instanzmethoden), aber keine andere Instanz von obj.classhat diese Methoden hinzugefügt.
  • extend ist eine öffentliche Methode

include - Standardmäßig werden die Methoden des angegebenen Moduls als Instanzmethoden im Zielmodul / in der Zielklasse gemischt. z.B

  • Wenn Sie aufrufen class Klazz; include Mod; end;, haben jetzt alle Instanzen von Klazz Zugriff auf Mods Methoden (als Instanzmethoden).
  • include ist eine private Methode, da sie innerhalb der Containerklasse / des Containermoduls aufgerufen werden soll.

Module überschreiben jedoch sehr oft dasinclude Verhalten, indem sie die includedMethode mit Affen patchen . Dies ist im alten Rails-Code sehr wichtig. Weitere Details von Yehuda Katz .

Weitere Details zu include, mit dem Standardverhalten, vorausgesetzt, Sie haben den folgenden Code ausgeführt

class Klazz
  include Mod
end
  • Wenn Mod bereits in Klazz oder einem seiner Vorfahren enthalten ist, hat die include-Anweisung keine Auswirkung
  • Es enthält auch Mods Konstanten in Klazz, solange sie nicht zusammenstoßen
  • Es gibt Klazz Zugriff auf Mods Modulvariablen, z. B. @@foooder@@bar
  • löst ArgumentError aus, wenn zyklische Includes vorhanden sind
  • Hängt das Modul als unmittelbaren Vorfahren des Anrufers an (dh es fügt Mod zu Klazz.ancestors hinzu, aber Mod wird nicht zur Kette von Klazz.superclass.superclass.superclass hinzugefügt. Wenn Sie also superKlazz # foo aufrufen, wird vor dem Überprüfen nach Mod # foo gesucht zu Klazz 'foo-Methode der echten Superklasse. Siehe RubySpec für Details.).

Natürlich ist die Ruby Core-Dokumentation immer der beste Ort für diese Dinge. Das RubySpec-Projekt war auch eine fantastische Ressource, da es die Funktionalität genau dokumentierte.

John Douthat
quelle
22
Ich weiß, dass dies ein ziemlich alter Beitrag ist, aber die Klarheit der Antwort konnte mich nicht davon abhalten, Kommentare abzugeben. Vielen Dank für eine nette Erklärung.
MohamedSanaulla
2
@anwar Natürlich, aber jetzt kann ich einen Kommentar abgeben und habe es geschafft, den Artikel wieder zu finden. Es ist hier verfügbar: aaronlasseigne.com/2012/01/17/explaining-include-and-extend und ich denke immer noch, dass das Schema das Verständnis viel einfacher macht
systho
1
Der große Vorteil dieser Antwort besteht darin, wie Methoden je nach Verwendung extendals Klassen- oder Instanzmethoden angewendet werden können . Klass.extend= Klassenmethoden, objekt.extend= Instanzmethoden. Ich habe immer (fälschlicherweise) angenommen, dass Klassenmethoden von extendund Instanz von stammen include.
Frank Koehl
16

Das ist richtig.

Hinter den Kulissen befindet sich ein Alias ​​für append_features , der (aus den Dokumenten):

Die Standardimplementierung von Ruby besteht darin, die Konstanten, Methoden und Modulvariablen dieses Moduls zu aModule hinzuzufügen, wenn dieses Modul noch nicht zu aModule oder einem seiner Vorfahren hinzugefügt wurde.

Toby Hede
quelle
4

Wenn Sie includeein Modul in eine Klasse einbinden, werden die Modulmethoden als Instanzmethoden importiert .

Wenn Sie jedoch extendein Modul in eine Klasse einbinden, werden die Modulmethoden als Klassenmethoden importiert .

Zum Beispiel, wenn wir ein Modul Module_testwie folgt definiert haben:

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

Nun zum includeModul. Wenn wir die Klasse Awie folgt definieren:

class A
  include Module_test
end

a = A.new
a.func

Die Ausgabe lautet : M - in module.

Wenn wir die Zeile include Module_testdurch ersetzen extend Module_testund den Code erneut ausführen, wird die folgende Fehlermeldung angezeigt : undefined method 'func' for #<A:instance_num> (NoMethodError).

Wenn Sie den Methodenaufruf a.funcin A.funcändern, ändert sich die Ausgabe in : M - in module.

Aus der obigen Codeausführung geht hervor, dass wenn wir includeein Modul sind, seine Methoden zu Instanzmethoden werden und wenn wir extendein Modul sind, seine Methoden zu Klassenmethoden werden .

Chintan Gandhi
quelle
3

Alle anderen Antworten sind gut, einschließlich des Tipps zum Durchsuchen von 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

Wie für Anwendungsfälle:

Wenn Sie das Modul ReusableModule in die Klasse ClassThatIncludes aufnehmen, wird auf die Methoden, Konstanten, Klassen, Submodule und andere Deklarationen verwiesen.

Wenn Sie die Klasse ClassThatExtends mit dem Modul ReusableModule erweitern, werden die Methoden und Konstanten kopiert . Wenn Sie nicht aufpassen, können Sie natürlich viel Speicher verschwenden, indem Sie Definitionen dynamisch duplizieren.

Wenn Sie ActiveSupport :: Concern verwenden, können Sie mit der Funktion .included () die Include-Klasse direkt neu schreiben. Das Modul ClassMethods in einem Concern wird in die Inclusive- Klasse erweitert (kopiert).

Ho-Sheng Hsiao
quelle
1

Ich möchte auch den Mechanismus erklären, wie er funktioniert. Wenn ich nicht richtig liege, bitte korrigieren.

Wenn wir verwenden include, fügen wir eine Verknüpfung aus unserer Klasse zu einem Modul hinzu, das einige Methoden enthält.

class A
include MyMOd
end

a = A.new
a.some_method

Objekte haben keine Methoden, nur Klassen und Module. Wenn also aeine Nachricht empfangen wird some_method, beginnt die Suchmethode some_methodin der Eigenklasse a, dann in der AKlasse und dann in den mit AKlassenmodulen verknüpften Modulen, falls vorhanden (in umgekehrter Reihenfolge, zuletzt eingeschlossene Gewinne).

Wenn wir verwenden extend, fügen wir einem Modul in der Eigenklasse des Objekts eine Verknüpfung hinzu. Wenn wir also A.new.extend (MyMod) verwenden, fügen wir unserem Modul eine Verknüpfung zur Instanzeigenklasse oder -klasse von A hinzu a'. Und wenn wir A.extend (MyMod) verwenden, fügen wir eine Verknüpfung zur A-Eigenklasse (Objekte, Klassen sind auch Objekte) hinzu A'.

Der Pfad für die Methodensuche alautet also wie folgt: a => a '=> verknüpfte Module mit einer' Klasse => A.

Es gibt auch eine Prepend-Methode, die den Suchpfad ändert:

a => a '=> vorangestelltes Modul für A => A => enthaltenes Modul für A.

Entschuldigung für mein schlechtes Englisch.

user1136228
quelle