next up previous contents index
Search Next: Fortgeschrittene Konzepte Up: Objektorientiertes Programmieren in Ruby Previous: Klassenvariablen und Vererbung   Contents   Index

Module


\epsfig{height=24pt,file=images/ruby.eps}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.


\epsfig{height=36pt,file=images/chan.eps}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. \epsfig{height=10pt,file=images/ruby.eps}

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.


\epsfig{height=36pt,file=images/chan.eps}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. \epsfig{height=10pt,file=images/ruby.eps}

Für die Namensraumhygiene (aber nicht nur) stellt Ruby noch die Methode alias bereit. [*]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.



Footnotes

... Module[*]
Module können nicht instanziert werden
... Man[*]
remove_method
... Date[*]
lib/date.rb
... "`Einmisch"'-Technik[*]
Module mischen sich ein
...alias [*]
Zweiter Name für das gleiche Objekt

next up previous contents index
Search Next: Fortgeschrittene Konzepte Up: Objektorientiertes Programmieren in Ruby Previous: Klassenvariablen und Vererbung   Contents   Index
(C) 2002 by dpunkt.de, Armin Roehrl, Stefan Schmiedl, Clemens Wyss 2002-01-20