2013-07-09 17 views
14

Ich habe ein paar Artikel über Ruby Mixin Methoden, extend und include gelesen, und ich bin immer noch nicht ganz sicher über das Verhalten. Ich verstehe, dass extend die Instanzmethoden des gegebenen Moduls als Singleton-Methoden zu dem Modul hinzufügen, das die Erweiterung durchführt, und dass include im Wesentlichen den Inhalt eines Moduls (Methoden, Konstanten, Variablen) demjenigen anfügen wird, der das Includeing durchführt, effektiv definiert sie im Empfänger.Ruby Mixins: erweitern und enthalten

Aber nach ein wenig Basteln, versuchen, ein Gefühl dafür zu bekommen, wie sich das Verhalten manifestieren wird, habe ich ein paar Fragen. Hier ist mein Test-Setup:

module Baz 
    def blorg 
    puts 'blorg' 
    end 
end 

module Bar 
    include Baz 
    def blah 
    puts 'blah' 
    end 
end 

module Foo 
    extend Bar 
end 

class Bacon 
    extend Bar 
end 

class Egg 
    include Bar 
end 

So wie ich erwarten würde, Modul Bar gewinnt die Instanzmethoden definiert in Baz (#blorg), als ob sie in sich selbst definiert worden war aufgrund der Einbeziehung Methode und Klasse Bacon Gewinne die Singleton-Methoden Bacon::blah und Bacon::blorg durch Erweiterung.

Bacon.blah # => blah 
Bacon.blorg # => blorg 

und Klasse Egg gewinnt die definierten Methoden in Bar (#blah und jetzt #blorg) als Instanzmethoden.

Egg.new.blah # => blah 
Egg.new.blorg # => blorg 

Ich bekomme alles, also ist das gut.

Allerdings verstehe ich nicht die Antworten, die ich von den Methoden #ancestors und #is_a? erhalten.

Bacon.ancestors # => [Bacon, Object, Kernel, BasicObject] 
Bacon.is_a? Bar # => true 

Egg.ancestors # => [Egg, Bar, Baz, Object, Kernel, BasicObject] 
Egg.is_a? Bar # => false 

Es scheint, dass ein Modul zur Verlängerung des #is_a? Methode verursacht true zurückzukehren, wenn über das Modul abgefragt, aber es ist nicht auf die Vorfahren der Klasse, und umgekehrt in Bezug auf die Aufnahme nicht hinzugefügt: die Vorfahren Die Klasse enthält die Module, die enthalten sind, aber die Methode #is_a? gibt false zurück, wenn sie abgefragt wird. Warum passiert das?

+0

+1 für das großartige Format dieser Frage. – sargas

Antwort

24

Der Unterschied ist, dass include die eingeschlossene Klasse zu den Vorfahren der einschließenden Klasse hinzufügen wird, während extend die erweiterte Klasse den Vorfahren der Singleton-Klasse der erweiternden Klassen hinzufügen wird. Puh.Lassen Sie uns zunächst beobachten, was passiert:

Bacon.ancestors 
#=> [Bacon, Object, Kernel, BasicObject] 

Bacon.singleton_class.ancestors 
#=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject] 

Bacon.new.singleton_class.ancestors 
#=> [Bacon, Object, Kernel, BasicObject] 

Bacon.is_a? Bar 
#=> true 

Bacon.new.is_a? Bar 
#=> false 

Und für die Egg Klasse

Egg.ancestors 
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject] 

Egg.singleton_class.ancestors 
#=> [Class, Module, Object, Kernel, BasicObject] 

Egg.new.singleton_class.ancestors 
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject] 

Egg.is_a? Bar 
#=> false 

Egg.new.is_a? Bar 
#=> true 

Was foo.is_a? Klass tatsächlich tut, ist zu prüfen, ob foo.singleton_class.ancestors enthält Klass. Die andere Sache ist, dass alle Vorfahren einer Klasse Vorfahren der Singleton-Klasse einer Instanz werden, wenn die Instanz erstellt wird. Also wird dies für alle neu erstellten Instanzen einer beliebigen Klasse als wahr gewertet:

Also was bedeutet das alles? extend und include auf verschiedenen Ebenen das gleiche tun, ich das folgende Beispiel Hoffnung macht dies deutlich, da beide Möglichkeiten, um eine Klasse zu erweitern sind im Wesentlichen gleichwertig:

module A 
    def foobar 
    puts 'foobar' 
    end 
end 

class B 
    extend A 
end 

class C 
    class << self 
    include A 
    end 
end 

B.singleton_class.ancestors == C.singleton_class.ancestors 
#=> true 

wo class << self nur die ungeraden Syntax ist auf die Singleton-Klasse zu erhalten . So ist extend wirklich nur eine Abkürzung für include in der Singleton-Klasse.

0
Egg.is_a? Egg # => false 

Die Include (effektiv) ändert Instanzen der Egg Klasse. Obwohl es nicht ganz dasselbe ist, ist es sehr ähnlich so etwas wie

class Egg < Bar 
end 

zu tun Wenn die erweitern Klasse Methoden hinzufügen, so ist dies sehr ähnlich so etwas wie

class Bacon 
    class << self 
    include Bar 
    end 
end 

zu tun können Sie Denken Sie darüber nach, wie Änderungen Instanzen der Klasse enthalten, wobei, wie erweitert, tatsächlich die Klasse ändert.

+0

vielleicht haben Sie etwas falsch eingegeben, aber wenn ich 'Egg.new.is_a? Ei "es ist wahr". Meintest du "Egg.is_a"? Ei # => falsch? – DesAdams

+0

Ja, habe ich getan. Ich werde das bearbeiten. – Olives