2008-10-01 6 views
344

Ich habe nur meinen Kopf um Ruby Metaprogrammierung. Die Mixin/Module schaffen es immer wieder, mich zu verwirren.Was ist der Unterschied zwischen Einschließen und Erweitern in Ruby?

  • umfassen: Mischungen in spezifizierten Modul Methoden wie Instanzmethoden in der Zielklasse
  • erstrecken: Mischungen in spezifizierten Modul Methoden als Klassenmethoden in der Zielklasse

Also ist der Hauptunterschied gerade das oder ist ein größerer Drache lauern? 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!" 
+0

Überprüfen Sie diesen Link auch: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/ – Donato

Antwort

209

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

Wenn Sie eine Klasse Klazz und Modul Mod, einschließlich Mod in Klazz gibt Fälle von Klazz Zugriff auf Mod ‚s Methoden. Oder Sie können Klazz mit Mod erweitern, was die KlasseKlazz Zugriff auf Mod Methoden gibt. Aber Sie können auch ein beliebiges Objekt mit o.extend Mod erweitern. In diesem Fall erhält das individuelle Objekt die Methoden Mod, obwohl alle anderen Objekte mit der gleichen Klasse wie o dies nicht tun.

13

Das stimmt.

Hinter den Kulissen sind ist eigentlich ein Alias ​​für append_features, die (von der Dokumentation):

Rubys Standardimplementierung ist die Konstanten hinzufügen, Methoden und Modul Variablen dieses Moduls zu aModule wenn Dieses Modul wurde nicht bereits zu einem Modul oder einem seiner Vorfahren hinzugefügt.

274

erstrecken - fügt die Methoden und Konstanten des angegebenen Moduls an die Metaklasse Ziel (das heißt die Singletonklasse) z.B.

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

umfassen - Standardmäßig mischt es in den Methoden des angegebenen Moduls als Instanzmethoden in der Ziel Modul/Klasse. z.B.

  • wenn Sie class Klazz; include Mod; end; nennen, jetzt alle Instanzen von Klazz haben Zugriff auf Mod-Methoden (wie zB Methoden)
  • include eine private Methode ist, weil es beabsichtigt ist aus dem Container-Klasse/Modul aufgerufen werden.

jedoch, Module sehr häufig Überschreibunginclude ‚Verhalten von Affen-Patching der included Methode. Dies ist im Legacy-Rails-Code sehr ausgeprägt. more details from Yehuda Katz.

Weitere Einzelheiten über include mit seinem Standardverhalten, vorausgesetzt, Sie den folgenden Code

class Klazz 
    include Mod 
end 
  • Wenn Mod bereits in Klazz enthalten ist, oder einer seiner Vorfahren, die Include-Anweisung nicht habe ausgeführt hat Effekt
  • Es enthält auch Mod-Konstanten in Klazz, solange sie nicht kollidieren
  • Es gibt Klazz Zugriff auf Mods Modulvariablen, z @@foo oder @@bar
  • Argument wirft, wenn es zyklisch sind beinhaltet
  • das Modul als die unmittelbaren Vorfahren des Anrufers Hängt (dh Es fügt Mod Klazz.ancestors, aber Mod ist nicht auf die Kette von Klazz.superclass.superclass.superclass hinzugefügt Wenn Sie also in Klazz # foo super aufrufen, werden Sie nach Mod # foo suchen, bevor Sie die foo-Methode von Klazz's echter Superklasse überprüfen (weitere Informationen finden Sie in der RubySpec-Datei).

Natürlich the ruby core documentation ist immer der beste Ort für diese Dinge zu gehen. The RubySpec project war auch eine fantastische Ressource, weil sie die Funktionalität genau dokumentierte.

+13

Ich weiß, das ist ziemlich alten Post, aber die Klarheit der Antwort konnte mich nicht davon abhalten, zu kommentieren. Vielen Dank für eine nette Erklärung. – MohamedSanaulla

+0

[RubySpec] (http://www.rubyspec.org/) gestorben :( – wiseland

+0

@systho Dieser Link funktioniert jetzt nicht – Anwar

4

Alle anderen Antworten sind gut, einschließlich der Spitze durch RubySpecs zu graben:

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 enthalten Modul ReusableModule in der Klasse ClassThatIncludes, auf die Methoden, Konstanten, Klassen, Submodule und andere Deklarationen verwiesen werden.

Wenn Sie Klasse ClassThatExtends mit Modul ReusableModule erweitern , dann bekommt die Methoden und Konstanten kopiert. Wenn Sie nicht vorsichtig sind, 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 einschließende Klasse direkt umschreiben. Modul ClassMethods in einem Concern wird erweitert (kopiert) in die Include-Klasse.

1

Ich habe es vorher gelernt, aber ich schätze es, wenn ich es benutze. Hier ist der Unterschied:

Dies funktioniert nicht, aber funktionieren würde, wenn ich habe es als def page_views(campaign) definiert:

class UserAction 
    include Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 

Dies funktioniert:

class UserAction 
    extend Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 
1

Ich würde auch die erläutern, Mechanismus wie es funktioniert. Wenn ich nicht richtig bin, bitte korrigieren.

Wenn wir include verwenden, fügen wir eine Verknüpfung von 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. So, wenn a empfängt Nachricht some_method beginnt es Suchmethode some_method in a 's Eigenklasse, dann in A Klasse und dann in Verbindung mit A Klassenmodule, wenn es einige (in umgekehrter Reihenfolge, zuletzt enthalten Gewinne).

Wenn wir extend verwenden, fügen wir Verknüpfung zu einem Modul in der Eigenklasse des Objekts hinzu. Also, wenn wir A.new.extend (MyMod) verwenden, fügen wir Verknüpfung zu unserem Modul zu A-Instanz Eigenklasse oder a' Klasse. Und wenn wir A.extend (MyMod) verwenden, fügen wir Verknüpfung zu A (Objekt, Klassen sind auch Objekte) Eigenklasse A'.

so Methode Lookup Pfad für a sich wie folgt: a => a '=> verknüpften Module zu einer' class => A.

auch eine prepend Methode ist die Lookup-Pfad ändert:

a => a '=> vorangestellte Module zu A => A => enthaltenes Modul zu A

Entschuldigung für mein schlechtes Englisch.