Module
sind nichtinstanzierbare Klassen. Sie können weder
erben noch vererben. Sie sammeln Methoden, Klassen und Konstanten,
die mit include anderen Klassen und Objekten zur Verfügung
gestellt werden können, und dienen als Begrenzer im Namensraum.
In größeren Projekten kann es zum Problem werden, wenn die Programmierer zwei verschiedenen Klassen den gleichen Namen gegeben haben. Wie soll man die beiden voneinander unterscheiden? Ruby-Module realisieren so genannte Namensräume, die eine zusätzliche Möglichkeit zur Identifikation bieten.
Das folgende Beispiel zeigt zwei Module (in der gleichen Datei), die beide eine Klasse Ding enthalten. Wenn man den Modulnamen mit angibt, können beide Klassen gleichzeitig verwendet werden:
module DasEine
class Ding
def to_s; "ein Ding"; end
end
end
module DasAndere
class Ding
def to_s; "ein anderes Ding"; end
end
end
DasEine::Ding.new #-> ein Ding
DasAndere::Ding.new #-> ein anderes Ding
Der include-Befehl fügt die angegebenen Module
in die aktuelle Klassenhierarchie ein:
module A; def to_s; "a"; end; end module B; def to_s; "b"; end; end # Abstammung ohne Module class D; end D.ancestors #-> [D, Object, Kernel] # "normale" Unterklasse class E < D; end E.ancestors #-> [E, D, Object, Kernel] # Abstammung mit Modulen class C; include A, B; end C.ancestors #-> [C, B, A, Object, Kernel]
Damit hat include den Effekt, dass Modul-Präfixe
entfallen können, da die im eingebundenen Modul
verfügbaren Methoden und Konstanten genau so verwendet werden, wie es
bei "`normaler"' Vererbung der Fall ist. Wenn Ruby die
Implementation einer Methode sucht, werden die Klassen
der ancestors-Liste der Reihe nach kontrolliert.
Sollten also gleich lautende Methoden oder Konstante vorhanden
sein, so werden diejenigen des zuletzt eingebundenen Moduls
verwendet.
Man
kann mit remove_method :methode
nicht benötigte Methoden entfernen.
Wenn man obiges Beispiel weiterführt:
c = C.new #-> b module B; remove_method :to_s; end c #-> a module A; remove_method :to_s; end c #-> #<C:...>Im Gegensatz dazu sorgt die Methode
undef_method dafür,
dass auch dann ein NoMethodError ausgelöst wird, wenn die angegebene
Methode in einer Oberklasse verfügbar ist.
Die in einem Modul definierten Methoden arbeiten in der Umgebung,
in die sie eingebettet werden. Damit brauchen "`generische"' Implementationen
in Ruby nur einmal vorgenommen werden.
Zum Beispiel werden im Modul Comparable alle Vergleichsoperatoren
mit dem allgemeinen Vergleich <=> definiert und können in
jeder Klasse verwendet werden, die über diese Methode verfügt: Das Modul
Date
der Standardbibliothek implementiert lediglich <=> und durch
das Einbinden des Comparable-Moduls stehen alle Vergleiche für Date-Instanzen
zur Verfügung:
require "date" a = Date.new(1999, 10, 22) #<Date:...> h = Date.new(1998, 5, 5) #<Date:...> x = Date.new(2002, 5, 18) #<Date:...> a.between?(h, x) #-> true
Dieser "`Einmisch"'-Technik
verdanken Module auch einen Spitznamen: Man spricht vom Mixin.
Ruby bietet mit seinen Modulen eine elegante Lösung für die Probleme, die sich eine Sprache normalerweise mit dem Verzicht auf Mehrfachvererbung einhandelt, auch wenn es umstritten ist, ob Mehrfachvererbung wirklich Vorteile bringt.
Java oder Delphi benötigen hier viele explizite Typumwandlungen, C++ hat Mehrfachvererbung.
Für die Namensraumhygiene (aber nicht nur) stellt Ruby noch die
Methode alias bereit.
![[*]](footnote.png)
alias erlaubt es, für globale Variablen und Methoden einen
weiteren Namen einzuführen.
class String
alias suchen index
end
"Rubychan".suchen("chan") #-> 4
$s = "Ruby Rules"
alias $t $s
$s.delete!("R")
$s #-> "uby ules"
$t #-> "uby ules"
Alias erstellt keine Kopie der ursprünglichen Definition, sondern ist
lediglich ein zweiter Verweis auf das Objekt. Auch hier
werden sehr elegante Codekonstruktionen möglich,
siehe die Implementation von synchronizeMethod in Abschnitt 17.2.