Die Programmiersprache Ruby (German Edition)
explizit definieren oder einfach unsere Zeile
attr_reader
umändern in
attr_accessor
:
attr_accessor :x, :y
Als Nächstes möchten wir eine Alternative zum Operator
+
haben, für den Fall, dass wir die Koordinaten des Punkts
p
zu denen des Punkts
q
addieren, dabei aber Punkt
p
verändern wollen, statt ein neues
Point
-Objekt zu erzeugen und zurückzugeben. Wir werden diese Methode
add!
nennen, wobei das Ausrufezeichen darauf hinweist, dass der interne Zustand des aufgerufenen Objekts verändert wird:
def add!(p) # Addiere p zu self, liefere verändertes self zurück
@x += p.x
@y += p.y
self
end
Wenn wir eine verändernde Methode definieren, fügen wir normalerweise nur dann ein Ausrufezeichen an den Namen an, wenn es auch eine nicht verändernde Version der gleichen Methode gibt. In diesem Fall ist der Name
add!
sinnvoll, wenn wir auch eine Methode
add
definieren, die ein neues Objekt zurückgibt, statt ihren Empfänger zu bearbeiten. Eine nicht verändernde Version einer verändernden Methode wird häufig einfach erstellt, indem eine Kopie von
self
erzeugt und die verändernde Methode für das kopierte Objekt aufgerufen wird:
def add(p) # Eine nicht verändernde Version von add!
q = self.dup # Erzeuge eine Kopie von self
q.add!(p) # Aufruf der verändernden Methode für die Kopie
end
In diesem trivialen Beispiel funktioniert unsere Methode
add
genau wie der Operator
+
, den wir schon definiert haben, also ist sie eigentlich nicht notwendig. Wenn wir also kein nicht veränderndes
add
schreiben, sollten wir uns überlegen, das Ausrufezeichen bei
add!
wegzulassen und durch den Namen der Methode selbst (»add« statt »plus«) deutlich zu machen, dass es sich um eine verändernde Methode handelt.
7.1.12 Veränderbare Klassen auf die Schnelle
Wenn Sie eine veränderbare Klasse
Point
haben wollen, können Sie diese (unter anderem) über
Struct
erzeugen.
Struct
ist eine Basisklasse in Ruby, die andere Klassen erzeugt. Diese generierten Klassen haben Akzessormethoden für die von Ihnen angegebenen Felder. Es gibt zwei Möglichkeiten, eine neue Klasse mit
Struct.new
zu erstellen:
Struct.new("Point", :x, :y) # Erstellt die neue Klasse Struct::Point
Point = Struct.new(:x, :y) # Erstellt die neue Klasse und weist sie Point zu
----
Benennen anonymer Klassen
Die zweite Codezeile nutzt ein ungewöhnliches Feature von Ruby-Klassen: Wenn Sie ein unbenanntes Klassenobjekt einer Konstanten zuweisen, wird der Name dieser Konstanten zum Namen einer Klasse. Sie können das Verhalten auch beobachten, wenn Sie den Konstruktor
Class.new
verwenden:
C = Class.new # Eine neue Klasse ohne Rumpf, einer Konstanten zugewiesen
c = C.new # Erstelle eine Instanz der Klasse
c.class.to_s # => "C": Name der Konstanten wird zum Klassennamen
----
Nachdem eine Klasse mittels
Struct.new
erzeugt worden ist, können Sie sie wie jede andere Klasse verwenden. Ihre Methode
new
erwartet Werte für jedes der benannten Felder, das Sie angeben, und ihre Instanzmethoden stellen Lese- und Schreibakzessoren für diese Felder bereit:
p = Point.new(1,2) # => #
p.x # => 1
p.y # => 2
p.x = 3 # => 3
p.x # => 3
Structs definieren die Operatoren
[]
und
[]=
für Array- und Hash-Indexierung. Zudem gibt es sogar
each
- und
each_pair
-Iteratoren, um über die Werte zu laufen, die in einer Instanz des Struct vorhanden sind:
p[:x] = 4 # => 4: wie p.x =
p[:x] # => 4: wie p.x
p[1] # => 2: wie p.y
p.each {|c| print c} # Ausgabe: "42"
p.each_pair {|n,c| print n,c } # Ausgabe: "x4y2"
Struct-basierte Klassen haben einen funktionierenden Operator
==
, können als Hash-Schlüssel verwendet werden (allerdings muss man ein wenig aufpassen, weil sie veränderbar sind) und definieren sogar eine nützliche Methode
to_s
:
q = Point.new(4,2)
q == p # => true
h = {q => 1} # Erzeugt einen Hash mit q als Schlüssel
h[p] # => 1: Wert für den Schlüssel p auslesen
q.to_s # => "#"
Eine Klasse
Point
, die als Struct definiert wurde, besitzt keine punktspezifischen Methoden wie
add!
oder den Operator
<=>
, der weiter oben in diesem Kapitel definiert wurde. Es gibt allerdings keinen Grund, sie nicht einfach hinzuzufügen. Klassendefinitionen sind in Ruby nicht statisch. Jede Klasse (auch solche, die per
Struct.new
definiert wurden) kann »geöffnet« werden und zusätzliche Methoden erhalten. Hier sehen Sie eine Klasse
Point
, die zunächst als
Struct
definiert und dann um punktspezifische Methoden ergänzt wird:
Point = Struct.new(:x, :y) #
Weitere Kostenlose Bücher