Bei der Definition einer Klasse muss man sich entscheiden, wie die neue Klasse
mit dem Rest des Ruby-Systems integriert werden soll. Sollen andere
Ruby-Objekte als Klassen- oder Instanzvariablen eingebettet werden
(Komposition)? Soll die Klasse direkt von einer anderen
Klasse abgeleitet werden (Vererbung)? Letzeren Fall kann man
in der LogHash-Klasse in Kapitel 14 begutachten.
Bei der direkten Ableitung müssen nur die Methoden neu definiert werden, die
eine Erweiterung oder Änderung des
Verhaltens der vorhandenen Klasse erfordern. Um eine schon vorhandene Methode
der Oberklasse aufzurufen, verwendet man super mit den
entsprechenden Argumenten.
class Punkt3d
attr_reader :x, :y, :z, :name
@@typ = "Punkt"
def initialize(name, x, y, z)
@x = x
@y = y
@z = z
@name = name
end
def to_s
"%s(%6.2f/%6.2f/%6.2f)" % [@name, @x, @y, @z]
end
end
class Punkt2d < Punkt3d
def initialize(name, x, y)
super(name, x, y, 0)
end
def to_s
"%s(%6.2f/%6.2f)" % [@name, @x, @y]
end
end
class Strecke
attr_reader :a, :b, :name
@@typ = "Strecke"
def initialize(name, a, b)
@name = name
@a = a
@b = b
end
def to_s
"%s[%s/%s]" % [@name, @a, @b]
end
end
p = Punkt2d.new("P", 1, 1)
q = Punkt2d.new("Q", 2, 3)
puts s = Strecke.new("s", p, q)
#-> s[P( 1.00/ 1.00)/Q( 2.00/ 3.00)]
Das erneute Definieren einer Klasse wird als inkrementelle Änderung aufgefasst, so dass eigene Methoden jederzeit hinzugefügt, aber auch überschrieben - d.h. neu definiert - werden können.
Der erste Vergleich im folgenden Beispiel verwendet die von der Object-Klasse
ererbte Methode ==, mit der Objektidentität geprüft wird. Nach
Erweiterung der Klasse Punkt3d wird eine geometrische Interpretation der
Gleichheit verwendet, so dass gleiche Koordinaten für die Gleichheit der Punkte
ausreichen.
puts p == Punkt2d.new("P", 1, 1) #-> false
class Punkt3d
def == (p)
x == p.x and y == p.y and z == p.z
end
end
puts p == Punkt2d.new("P", 1, 1) #-> true
Im Gegensatz zu Java sind auch die eingebauten Ruby-Klassen nicht "`final"', also unveränderbar, sondern können bei Bedarf angepasst werden.
Bei Bedarf kann man eine Klasse mit freeze fixieren, so dass
keine Änderungen mehr vorgenommen werden können. Jedoch wird beim
Duplizieren mit dup stets eine nicht eingefrorene Kopie erzeugt,
so dass man immer eine modifizierbare Klasse erhalten kann.
class Foo def to_s; "Foo"; end end Foo.freeze Foo.new #-> Foo # Änderung der Klasse ist nicht möglich class Foo def to_s; "Bar"; end #-> TypeError end # die Kopie ist wieder wandelbar MyFoo = Foo.dup class MyFoo def to_s; "Bar"; end end MyFoo.new #-> Bar
Erweiterungen sind auch bei eingebauten Klassen möglich. Es wäre praktisch, wenn ein Array zählen könnte, wie oft es ein bestimmtes Objekt enthält. Dafür muss man nicht viel unternehmen:
class Array
def zaehle(x)
(select { |i| i == x }).size
end
end
a = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3]
a.zaehle 2 #-> 3
a.zaehle 88 #-> 0