2012-10-16 2 views
5

Angenommen, ich habe ein Modul namens Flight mit Klassen- und Instanzmethoden. Ich kann seine Methoden in einer Klasse erhalten mit include, extend oder beides:Warum funktioniert die Vererbung von Ruby-Modulen nicht wie eine Klassenvererbung?

class Bat < Mammal 
    # Add Flight's class methods to Bat. 
    extend Flight 

    # Add Flight's instance methods to Bat. 
    include Flight 
    ... 
end 

includeFlight zu Bat.ancestors hinzufügen, aber extend nicht.

Meine Frage ist, Warum ist das für Module anders als für Klassen? Wenn ich Mammal unterwerfe, immer erhalten Sie beide Klasse und Instanzmethoden auf einmal. Wenn ich jedoch ein Modul mische, kann ich nicht gleichzeitig Klassen- und Instanzmethoden abrufen (es sei denn, ich verwende den self.included Hook oder etwas wie ActiveSupport::Concern).

Gibt es hinter diesem Unterschied ein Problem mit dem Sprachdesign?

+1

Nun, hat es einige Unterschiede, um die Existenz eines gesonderten zu rechtfertigen Entität, richtig? :) –

Antwort

3

„Wenn ich Mammal Unterklasse, habe ich immer sowohl Klassen- und Instanzmethoden auf einmal“

Das ist, weil Bat Klasse als Objekt auch die Instanzmethoden von Mammal Singletonklasse geerbt.

Inheritance Graph

ein Modul in eine Klasse mit ein, ändert die Methode Kette nachzuschlagen. Also erbt die Klasse eigentlich keine Instanzmethoden.

Das Erweitern einer Klasse mit einem Modul ist dasselbe wie das Erweitern eines Objekts. Die Klasse erwirbt einfach die Instanzmethoden des Moduls als Klasseninstanzmethoden (dh Methoden auf dem Klassenobjekt selbst).

3

würde Ich mag einen Teil Ihrer Frage beantworten:

includeFlight zu Bat.ancestors hinzufügen, aber extend nicht.

verlängern ist nicht das gleiche wie enthalten, so tut es etwas anderes offensichtlich ... Sie können denken erweitern gleich einem sind, umfassen von der Klasse metaclass.

Werfen Sie einen Blick auf das folgende Beispiel:

module M 
end 

class A 
    include M 
end 

# then you will see M within A's ancestors as you know 
A.ancestors # => [A, M, Object...] 


class B 
    # the following is roughly the same as extend M: 
    class <<self 
    include M 
    end 
end 

# then you will see M within B's metaclass' ancestors 
MetaclassOfB = class <<B; self; end 
MetaclassOfB.ancestors # => [M, Class, Module...] 

Also, da verlängern ist wie ein auf der Metaklasse sind, können Sie die erweiterten Module in der Metaklasse Vorläuferkette zeigt sich sehen .. .

10

Sowohl Module#include und Object#extend sind die Instanzmethoden eines Module zu einem Object hinzuzufügen, verwendet wird. das Modul Gegeben:

module Flight 
    def can_fly? 
     true 
    end 
end 

Module#include verwendet wird, hinzuzufügen (oder Mischung in) die Instanzmethoden eines Moduls an die Instanz Methoden einer Klasse oder eines Moduls:

class Bat < Mammal 
    include Flight 
end 

a = Bat.new() 
a.can_fly?  # true 

es tatsächlich , wirkt sich auf die Object#is_a? Methode so:

a.is_a? Flight  # true 

Module#include a private Methode, so kann es nur mit Funktion Notation aufgerufen werden, wenn eine Klasse oder ein anderes Modul definieren:

class Bat < Mammal 
    self.include Flight  # NoMethodError: private method called 
end 

Object#extend fügt die Instanzmethoden eines Moduls als Singletons Methoden auf das Objekt, an dem es genannt, so können Sie dies tun:

b = Mammal.new() 
b.extend Flight 
b.can_fly?   # true 
b.is_a? Flight  # true 

c = Mammal.new() 
c.can_fly?   # NoMethodError: undefined method 

Und nur b werden die Instanzmethoden von Flight haben; andere Mammal Objekte werden nicht.

Beim Aufrufen von Object#extend innerhalb einer Klassendefinition werden die Methoden zur Eigenklasse der Klasse hinzugefügt, die Sie definieren. Dies ist der wesentliche Unterschied zwischen den beiden Methoden ist, wenn sie innerhalb einer Klassendefinition zu verwenden, da die Methoden als Klassenmethoden hinzugefügt werden:

class Bat < Mammal 
    extend Flight 
end 

Bat.can_fly?  # true 

d = Bat.new 
d.can_fly?  # NoMethodError: undefined method 
Verwandte Themen