Die Programmiersprache Ruby (German Edition)
die meisten Programmierer versuchen es zu vermeiden, dem globalen Namensraum Methoden hinzuzufügen. Eine bessere Lösung ist daher, die zwei Methoden innerhalb eines Moduls
Base64
zu definieren:
module Base64
def self.encode
end
def self.decode
end
end
Beachten Sie, dass wir unsere Methoden mit dem Präfix
self.
versehen haben, wodurch sie zu »Klassenmethoden« des Moduls werden. Wir könnten auch explizit den Modulnamen angeben und die Methoden wie folgt definieren:
module Base64
def Base64.encode
end
def Base64.decode
end
end
Eine Definition der Methoden auf diese Weise führt zwar zu Wiederholungen, spiegelt aber besser die Aufrufsyntax der Methoden wider:
# So rufen wir die Methoden des Moduls Base64 auf.
text = Base64.encode(data)
data = Base64.decode(text)
Beachten Sie, dass Modulnamen mit einem Großbuchstaben beginnen müssen, genau wie Klassennamen. Durch das Definieren eines Moduls wird eine Konstante mit dem gleichen Namen wie das Modul erzeugt. Der Wert dieser Konstante ist das
Module
-Objekt, das das Modul repräsentiert.
Module können auch Konstanten enthalten. Unsere Base64-Implementierung wird vermutlich eine Konstante nutzen, um die 64 Zeichen in einem String zu speichern, die für Base64 genutzt werden:
module Base64
DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
'abcdefghijklmnopqrstuvwxyz' \
'0123456789+/'
end
Außerhalb des Moduls
Base64
kann diese Konstante als
Base64::DIGITS
angesprochen werden. Innerhalb des Moduls können sich unsere Methoden
encode
und
decode
darauf einfach per
DIGITS
beziehen. Wenn die beiden Methoden eventuell gemeinsame veränderbare Daten teilen wollen, können sie eine Klassenvariable (mit einer Präfix
7
) nutzen — so wie in einer Klasse.
7.5.1.1 Verschachtelte Namensräume
Module (einschließlich Klassen) können verschachtelt werden. Das führt zu verschachtelten Namensräumen, hat aber keinen anderen Effekt: Eine Klasse oder ein Modul, die/das in einem/r anderen verschachtelt ist, hat keinen besonderen Zugriff auf die Klasse oder das Modul, in dem sie/es sich befindet. Um mit unserem Base64-Beispiel fortzufahren, nehmen wir nun an, dass wir spezielle Klassen für das Kodieren und Dekodieren schreiben wollen. Da die Klassen
Encoder
und
Decoder
auf jeden Fall miteinander in Beziehung stehen, wollen wir sie in einem Modul verschachteln.
module Base64
DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
class Encoder
def encode
end
end
class Decoder
def decode
end
end
# Eine Hilfsfunktion für beide Klassen
def Base64.helper
end
end
Indem wir unseren Code auf diese Weise strukturieren, haben wir zwei neue Klassen
Base64::Encoder
und
Base64::Decoder
definiert. Innerhalb des Moduls
Base64
können sich die beiden Klassen anhand ihrer nicht qualifizierten Namen aufeinander beziehen und müssen das Präfix
Base64
nicht mit angeben. Und jede der Klassen kann die Konstante
DIGITS
ohne ein Präfix verwenden.
Schauen Sie sich aber andererseits die Hilfsfunktion
Base64.helper
an. Die verschachtelten Klassen
Encoder
und
Decoder
haben keinen besonderen Zugriff auf die Methoden des Moduls und müssen diese Hilfsmethode über ihren vollständig qualifizierten Namen ansprechen:
Base64.helper
.
Da es sich bei Klassen um Module handelt, können sie ebenfalls verschachtelt werden. Das Einbetten einer Klasse in einer anderen betrifft nur den Namensraum der inneren Klasse — sie bekommt keinen besonderen Zugriff auf die Methoden oder Variablen der äußeren Klasse. Wenn Ihre Implementierung einer Klasse eine Hilfsklasse, eine Proxyklasse oder eine andere Klasse benötigt, die nicht Teil einer öffentlichen API ist, können Sie darüber nachdenken, diese internen Klassen in der Klasse einzubetten, die sie nutzt. Das hält den Namensraum sauber und aufgeräumt, macht die verschachtelten Klassen aber in keiner Weise privat.
In „7.9 Lookup von Konstanten“ finden Sie eine Erläuterung, wie Namen von Konstanten aufgelöst werden, wenn man Module verschachtelt.
7.5.2 Module als Mixins
Die zweite Anwendung von Modulen ist mächtiger als die erste. Wenn ein Modul Instanzmethoden statt Klassenmethoden definiert, können diese Instanzmethoden in andere Klassen »eingemixt« werden.
Enumerable
und
Comparable
sind bekannte Beispiele für Mixin-Module.
Enumerable
definiert nützliche Iteratoren, die in Begriffen eines
each
-Iterators implementiert sind.
Enumerable
definiert die Methode
each
nicht selber, aber jede Klasse, die sie definiert,
Weitere Kostenlose Bücher