Die Programmiersprache Ruby (German Edition)
von
class_eval
bringt den geringfügigen Zusatzaufwand mit sich, den Code-String zu parsen. Der Vorteil besteht jedoch darin, dass die Methoden, die wir definieren, selbst keine reflektiven APIs zu nutzen brauchen; sie können den Wert einer Instanzvariablen direkt lesen oder setzen.
class Module
private # Die beiden nachfolgenden Methoden sind privat.
# Diese Methode funktioniert wie attr_reader, aber hat einen
# kürzeren Namen.
def readonly(*syms)
return if syms.size == 0 # Keine Argumente? Nichts tun.
code = "" # Mit einem leeren Code-String beginnen.
# Einen String mit Ruby-Code erzeugen, der Attribut-Lese-
# methoden definiert. Beachten Sie, wie das Symbol in den Code-
# String interpoliert wird.
syms.each do |s| # Für jedes Symbol
code << "def #{s}; @#{s}; end\n" # Die Methodendefinition
end
# Zum Schluss class_eval der erzeugten Methode, um Instanz-
# methoden zu erzeugen.
class_eval code
end
# Diese Methode funktioniert wie attr_accessor, aber hat einen
# kürzeren Namen.
def readwrite(*syms)
return if syms.size == 0
code = ""
syms.each do |s|
code << "def #{s}; @#{s} end\n"
code << "def #{s}=(value); @#{s} = value; end\n"
end
class_eval code
end
end
Listing 8.6 Attributmethoden mit class_eval
8.10.2 Methoden mit define_method definieren
Listing 8.7 ist ein anderer Ansatz für Attributakzessoren. Die Methode
attributes
ist so etwas wie die in Listing 8.6 definierte Methode
readwrite
. Anstatt beliebig viele Attributnamen als Argumente anzunehmen, erwartet sie ein einzelnes Hash-Objekt. Dieser Hash sollte Attributnamen als Schlüssel enthalten und diese Attributnamen den Standardwerten für die Attribute zuordnen. Die Methode
class_attrs
funktioniert wie
attributes
, definiert aber Klassenattribute statt Instanzattributen.
Denken Sie daran, dass Ruby es erlaubt, die geschweiften Klammern um Hash-Literale wegzulassen, wenn diese das letzte Argument in einem Methodenaufruf sind. Deshalb könnte die Methode
attributes
mit Code wie diesem aufgerufen werden:
class Point
attributes :x => 0, :y => 0
end
In Ruby 1.9 können wir die knappere Hash-Syntax verwenden:
class Point
attributes x:0, y:0
end
Dies ist ein weiteres Beispiel, das sich Rubys flexible Syntax zunutze macht, um Methoden zu erzeugen, die sich wie Sprachschlüsselwörter verhalten.
Die Implementierung der Methode
attributes
in Listing 8.7 unterscheidet sich ein gutes Stück von derjenigen der Methode
readwrite
in Listing 8.6 . Anstatt einen String mit Ruby-Code zu erzeugen und ihn mit
class_eval
auszuwerten, definiert die Methode
attributes
den Rumpf des Attributakzessors in einem Block und definiert die Methoden mithilfe von
define_method
. Da dieses Methodendefinitionsverfahren uns nicht erlaubt, Bezeichner direkt in den Methodenrumpf zu interpolieren, müssen wir uns auf reflektive Methoden wie
instance_variable_get
verlassen. Deshalb sind die mit
attributes
definierten Akzessoren wahrscheinlich etwas weniger effizient als die mit
readwrite
definierten.
An der Methode
attributes
ist interessant, dass sie die Standardwerte für die Attribute nicht explizit in irgendeiner Art von Klassenvariablen speichert. Stattdessen wird der Standardwert für jedes Attribut vom Gültigkeitsbereich des Blocks eingefangen, der zur Definition der Methode verwendet wird. (Siehe „6.6 Closures“ für weitere Informationen über solche Closures.)
Die Methode
class_attrs
definiert Klassenattribute sehr einfach: Sie ruft
attributes
für die Eigenklasse der Klasse auf. Das bedeutet, dass die resultierenden Methoden Klasseninstanzvariablen (siehe „7.1.16 Klassen-Instanzvariablen“ ) anstelle regulärer Klassenvariablen verwenden.
class Module
# Diese Methode definiert Attribut-Reader und -Writer-Methoden für
# benannte Attribute, erwartet aber ein Hash-Argument, das den
# Attributnamen Standardwerte zuordnet. Die erzeugte Attribut-
# Reader-Methode gibt den Standardwert zurück, wenn die Instanz-
# variable noch nicht definiert wurde.
def attributes(hash)
hash.each_pair do |symbol, default| # Für jedes Attribut/Wert-Paar
getter = symbol # Name der Getter-Methode
setter = :"#{symbol}=" # Name der Setter-Methode
variable = :"@#{symbol}" # Name der Instanzvariablen
define_method getter do # Getter-Methode definieren
if instance_variable_defined? variable
instance_variable_get variable # Variable liefern, wenn definiert
else
default # Andernfalls Standardwert zurückgeben
end
end
define_method setter
Weitere Kostenlose Bücher