|
|||
Song
,
[Wie wir schon auf Seite 9 sagten,
fangen Klassen-Namen mit einem Großbuchstaben an und
Methodennamen mit einem Kleinbuchstaben..]
die eine einzige Methode enthält, nämlich initialize
.
class Song def initialize(name, artist, duration) @name = name @artist = artist @duration = duration end end |
initialize
ist eine spezielle Methode in Ruby-Programmen. Wenn man
Song.new
aufruft, um ein neues
Song
-Objekt zu erzeugen, so erzeugt Ruby erst ein
uninitialisiertes Objekt und ruft dann die Methode initialize
auf, zusammen
mit den Parametern, die wir der new
-Methode mitgegeben haben.
Dies gibt einem die Möglichkeit, den Anfangszustand des Objekts im Code zu
bestimmen.
Bei der Klasse Song
benötigt die Methode initialize
drei Parameter. Diese Parameter funktionieren innerhalb der Methode
genau wie lokale Variablen, so dass sie genau wie diese auch mit
einem Kleinbuchstaben anfangen.
Jedes Objekt repräsentiert einen eigenen Song, deshalb muss jedes
unserer Song
-Objekte seinen eigenen
Namen, Künstler und Dauer mit sich tragen. Das heißt, dass wir diese
Variablen in Instanz-Variablen innerhalb des Objekts
speichern müssen.
In Ruby ist eine Instanz-Variable einfach ein Name, der mit einem ``at''-Zeichen
(``@'') beginnt. In unserem Beispiel ist der Parameter name
verknüpft mit
der Instanz-Variablen @name
,
artist
ist verknüpft mit @artist
, und duration
(die
Länge des Songs in Sekunden) ist verknüpft mit @duration
.
Testen wir also nun unsere schicke neue Klasse.
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.inspect |
» | "#<Song:0x4018bfc4 @duration=260, @artist=\"Fleck\", @name=\"Bicylops\">" |
inspect
, die an jedes mögliche Objekt gesendet werden kann,
die Id und die Instanz-Variablen aus. Es sieht so aus, als wär das alles
ganz korrekt.
Unser Erfahrung sagt uns, dass wir während der Entwicklung den
Inhalt eines Song
Objektes öfter ausgeben
werden, und inspect
s Default-Formatierung lässt etwas
zu wünschen übrig. Glücklicherweise gibt es in Ruby eine Standard-Nachricht,
to_s
, die an alle Objekte geschickt wird, die man als String
wiedergeben will.
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.to_s |
» | "#<Song:0x4018c1b8>" |
to_s
in unserer Klasse überschreiben.
Bevor wir das machen, sollten wir kurz darüber sprechen, wie wir
Klassendefinitionen in diesem Buch darstellen.
In Ruby sind Klassen niemals geschlossen: man kann jederzeit
Methoden zu einer existierenden Klasse hinzufügen. Das gilt für die
Klassen die man selber schreibt genauso wie für die Standard-, die
eingebauten Klassen. Wenn man neuen Inhalt spezifiziert, wird dieser
zu dem vorhandenen hinzugefügt.
Für unser Vorhaben ist das prima. Während wir dieses Kapitel
abhandeln und dabei Eigenschaften zu unseren Klassen hinzufügen,
werden wir nur die Klassendefinitionen für die neuen Methoden
zeigen; die alten sind aber immer noch da. Wir brauchen dann nicht
so viel redundantes Zeug in jedem Beispiel zu wiederholen. Wenn man
natürlich diesen Code mal eben schnell aus dem Ärmel schüttelt, wird man wahrscheinlich alles zusammen in eine einzelne
Klassendefinition packen.
Genug der Einzelheiten, gehen wir zurück zum Hinzufügen der
to_s
-Methode unserer Song
-Klasse.
class Song |
||
def to_s |
||
"Song: [email protected][email protected]} ([email protected]})" |
||
end |
||
end |
||
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.to_s |
» | "Song: Bicylops--Fleck (260)" |
to_s
-Methode bei
allen Objekten unterstützt, aber wir sagten nicht wie. Die Antwort hat etwas
mit Vererbung, Unterklassen und der Art zu tun, mit der Ruby feststellt,
welche Methode denn nun laufen soll, wenn man eine Meldung zu einem
Objekt schickt. Die ist das Thema eines neuen Abschnitts, also ...
Song
.
Dann kommt die Marketingabteilung und erzählt uns, dass wir auch noch
Karaoke unterstützen müssen. Ein Karaoke-Song ist genau wie ein normaler
auch (es gibt da natürlich keinen Gesang, aber das braucht uns nicht zu
interessieren). Allerdings gibt es dazu einen Satz von Textzeilen (lyrics), zusammen
mit Zeitangaben zur Synchronisation. Wenn unsere Jukebox einen
Karaoke-Song spielt, sollte der Zeilentext über den Bildschirm an der
Vorderseite der Jukebox flimmern und das auch noch synchron zur Musik.
Eine Herangehensweise an dieses Problem ist die Definition einer neuen
Klasse, KaraokeSong
, die genauso ist wie die
Song
-Klasse, nur mit einem zusätzlichen Lauftext.
class KaraokeSong < Song def initialize(name, artist, duration, lyrics) super(name, artist, duration) @lyrics = lyrics end end |
< Song
'' in der Zeile mit der Klassendefinition dagt Ruby, dass
ein KaraokeSong
eine Unterklasse von
Song
ist
(Das heißt auch, dass Song
eine Oberklasse von
KaraokeSong
ist, wer hätte das gedacht. Man spricht auch von Eltern-Kind Beziehungen, so dass
KaraokeSong
s
Elter dann Song
wäre.) Fürs Erste stören wir uns nicht weiter
an der initialize
-Methode, über diesen super
-Aufruf sprechen wir
später.
Jetzt erzeugen wir einen KaraokeSong
und prüfen, ob unser Code
funktioniert. (In dem endgülitngen System wird der Text innerhalb eines Objekts stecken, das
den Text und die Synchronisationsinformationen enthält. Um unsere Klasse zu testen, benutzen
wir erstmal einen String. Das ist einer der Vorteile einer typ-losen Sprache -- man braucht nicht
alles zu definieren, bevor man den Code laufen lässt.)
aSong = KaraokeSong.new("My Way", "Sinatra", 225, "And now, the...") |
||
aSong.to_s |
» | "Song: My Way--Sinatra (225)" |
to_s
-Methode den
Text nicht an?
Dei Antwort hat etwas damit zu tun, wie Ruby feststellt, welche Methode
dran ist, wenn man eine Meldung an ein Objekt schickt. Zu dem Zeitpunkt, an
dem Ruby den Methodenaufruf aSong.to_s
compiliert, weiß es noch
gar nicht, wo die Methode zu finden ist. Stattdessen verschiebt es diese
Entscheidung auf später, wenn das Programm läuft. Es schaut sich dann
die Klasse aSong
an. Wenn in der Klasse eine Methode mit dem
selben Namen wie die Meldung implementiert ist, so wird diese Methode
benutzt. Andernfalls sucht Ruby nach eine Methode in der Elternklasse, dann
in der Großelternklasse und so weiter die ganze Leiter der Vorfahren hinauf.
Wenns keine Vorfahren mehr gibt und Ruby keine passende Methode
gefunden hat, so läuft dann eine spezielle Aktion ab, in deren Verlauf
normalerweise ein Fehler geraist wird. [Natürlich kann man einen
solchen Fehler abfangen. Damit kann man dann auch Methoden zur
Laufzeit simulieren. das wird beschrieben unter
Object#method_missing
auf Seite 360.]
Nun als zurück zu unserem Beispiel. Wir schickten die Meldung to_s
an
aSong
, ein Objekt der Klasse KaraokeSong
.
Ruby schaut in KaraokeSong
nach eine Methode mit dem Namen
to_s
,
findet dort aber nichts. Der Interpreter sieht dann bei der Elternklasse von
KaraokeSong
nach, nämlich Song
, und findet dort die to_s
-Methode, die
wir auf Seite 20 definiert haben. Deshalb beschränkt sich die Ausgabe auf die Einzelheiten des
Songs und es gibt keinen Text --- die Klasse Song
weiß nichts von
irgendwelchen Texten.
Diese Problem beheben wir, indem wir KaraokeSong#to_s
implementieren. Es gibt mehrere Möglichkeiten das zu tun. Fangen wir mit der schlechteren an.
Wir kopieren die to_s
-Methode von Song
und fügen
einfach die Textbehandlung hinzu.
class KaraokeSong |
||
# ... |
||
def to_s |
||
"KS: [email protected][email protected]} ([email protected]}) [[email protected]}]" |
||
end |
||
end |
||
aSong = KaraokeSong.new("My Way", "Sinatra", 225, "And now, the...") |
||
aSong.to_s |
» | "KS: My Way--Sinatra (225) [And now, the...]" |
@lyrics
-Instanz-Variable wird richtig angezeigt.
Dazu greift die Subklasse direkt auf die Instanz-Vatiable ihres
Vorfahrens zu. Warum ist das also eine schlecht Art to_s
zu implementieren?
Die Antwort hat etwas mit gutem Programmier-Stil zu tun (und mit etwas, das
decoupling (Entkoppeln)
heißt).
Indem wir in dem internen Zustand unserer Elternklasse herumfuchteln, verbinden
wir uns fest mit deren Implementation. Wir könnten uns etwa entscheiden, dass
Song
von jetzt an die Song-Dauer in Millisekunden
speichern soll. Dann würde plötzlich KaraokeSong
unschöne
Werte zurückmelden. Der Gedanke einer Karaoke-Version von ``My Way'', die
3750 Minuten dauert, ist einfach zu schrecklich.
Wir umgehen dieses Problem, wenn jede Klasse ihren inneren Zustand
selber handhabt. Wenn KaraokeSong#to_s
aufgerufen
wird, müssen wir die to_s
-Methode des Elternteils aufrufen,
um die Song-Details zu bekommen. Daran werden dann die Text-Informationen
drangehängt und das ganze zurückgegeben. Der Trick dabei ist das
Ruby-Schlüselwort ``super
''. Wenn man super
ohne
Argumente aufruft, schickt Ruby eine Meldung zum Elternteil des aktuellen
Objekts und bittet um den Aufruf einer Methode mit dem selben Namen wie
die aktuelle Methode. Dabei kriegt die Elternmethode dieselben Parameter mit,
die die aktuelle Methode bekommen hatte. Damit können wir unsere neue und
verbesserte Methode to_s
implementieren.
class KaraokeSong < Song |
||
# Format ourselves as a string by appending |
||
# our lyrics to our parent's #to_s value. |
||
def to_s |
||
super + " [[email protected]}]" |
||
end |
||
end |
||
aSong = KaraokeSong.new("My Way", "Sinatra", 225, "And now, the...") |
||
aSong.to_s |
» | "Song: My Way--Sinatra (225) [And now, the...]" |
KaraokeSong
eine Subklasse
von Song
ist, aber wir haben keine Elternklasse für
Song
selber angegeben. Wenn man beim Definieren
einer Klasse keine Elternklasse angibt, nimmt Ruby automatisch die Klasse
Object
als Elternklasse und dadurch werden die
Instanz-Methoden von Object
für jedes andere
Objekt verfügbar. Das heißt dass alle Objekte Object
als
Vorfahren haben und dass die Instanz-Methoden von Object
für
alle Objekte verfügbar sind. Jetzt wissen wir die Antwort; to_s
ist eine
von mehr als 35 Instanz-Methoden der Klasse Object
. Die komplette
Liste fängt auf Seite 356 an.
Song
.
Song
-Objekte, die wir bisher
erzeugt haben, besitzen einen internen Zustand (wie der Song-Titel
und Künstler). Dieser Zustand ist privat zu diesen Objekten, kein
anderes Objekt kann auf diese Instanz-Variablen zugreifen. Normalerweise
ist das eine gute Sache. Es bedeutet, dass das Objekt selber
zuständig ist für die Aufrechterhaltung seiner eigenen
Konsistenz.
Natürlich ist ein Objekt, das völlig eingekapselt ist, etwas nutzlos --
man kann es erzeugen, aber dann kann man nichts mehr damit
anfangen. Normalerweise definiert man Methoden, um auf den Zustand
eines Objekts zuzugreifen und ihn zu verändern, damit die Außenwelt
mit dem Objekt interagieren kann. Diese von außen sichtbaren
Eigenschaften eines Objekts nennt man Attribute.
Bei unserem Song
-Objekt wäre das erste was
wir bräuchten die Fähigkeit, den Titel und den Künstler herauszufinden
(damit wir das anzeigen können, während der Song abgespielt wird),
sowie die Dauer (damit wir eine Art Fortschrittsbalken anzeigen können).
class Song |
||
def name |
||
@name |
||
end |
||
def artist |
||
@artist |
||
end |
||
def duration |
||
@duration |
||
end |
||
end |
||
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.artist |
» | "Fleck" |
aSong.name |
» | "Bicylops" |
aSong.duration |
» | 260 |
attr_reader
erzeugt die Zugriffs-Methode automatisch.
class Song |
||
attr_reader :name, :artist, :duration |
||
end |
||
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.artist |
» | "Fleck" |
aSong.name |
» | "Bicylops" |
aSong.duration |
» | 260 |
:artist
ist ein Ausdruck, der ein mit artist
verbundenes Symbol
-Objekt
zurückliefert. Man kann sich :artist
als name der Variablen artist
vorstellen, während das einfache artist
der Wert der Variablen ist. In
diesem Beispiel nannten wir die Zugriffs-Methoden name
, artist
und
duration
.
Die dazugehörenden Instanz-Variablen @name
, @artist
und
@duration
werden automatisch erzeugt. Diese Zugriffs-Methoden sind identisch
zu denen, die wir davor von Hand geschrieben haben.
Song
-Objekt speichern.
In Sprachen wie C++ und Java macht man dies mit Setter-Funktionen.
class JavaSong { // Java code private Duration myDuration; public void setDuration(Duration newDuration) { myDuration = newDuration; } } s = new Song(....) s.setDuration(length) |
aSong.name
. Also scheint es nur natürlich, den
Namen einer solchen Variablen zu benutzen, wenn man auf den
Wert eines Attributs zugreifen will. Dem Prinzip der geringsten
Überraschung folgend funktioniert das in Ruby genau so, wie uns das erhoffen.
class Song |
||
def duration=(newDuration) |
||
@duration = newDuration |
||
end |
||
end |
||
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.duration |
» | 260 |
aSong.duration = 257 # setzt in attribute den neuen Wert ein |
||
aSong.duration |
» | 257 |
aSong.duration = 257
'' ruft die Methode
duration=
des aSong
-Objekts auf und gibt 257
als
Argument mit. Es ist also so: wenn ein Methodenname mit einem Gleichheitszeichen
endet, dann kann dieser Name auf der linken Seite einer Zuweisung stehen.
Natürlich gibts bei ruby dafür wieder eine Abkürzung.
class Song attr_writer :duration end aSong = Song.new("Bicylops", "Fleck", 260) aSong.duration = 257 |
class Song |
||
def durationInMinutes |
||
@duration/60.0 # force floating point |
||
end |
||
def durationInMinutes=(value) |
||
@duration = (value*60).to_i |
||
end |
||
end |
||
aSong = Song.new("Bicylops", "Fleck", 260) |
||
aSong.durationInMinutes |
» | 4.333333333 |
aSong.durationInMinutes = 4.2 |
||
aSong.duration |
» | 252 |
durationInMinutes
ein Attribut wie jedes andere zu sein.
Intern gibt es dazu keine Instanz-Variable.
Das ist keinesfalls eine Seltsamkeit. In seinem wegweisenden
Buch
Object-Oriented Software Construction
nennt Bertrand Meyer dies das
Uniform Access Principle (Prinzip des einheitlichen Zugriffs).
Indem man den Unterschied zwischen Instanz-Variablen und
berechneten Werten verbirgt, trennt man den Rest der Welt ab von
der Implementation der Klasse. Dann hat man die Freiheit, interne
Abläufe zu ändern, ohne dass die Millionen von Code-Zeilen, die
diese Klasse benutzen, geändert werden müssen.
Das ist ein großer Gewinn.
@@count
''. Anders als
globale oder Instanz-Variablen müssen Klassen-Variablen vor Gebrauch
initialisiert werden. Oftmals ist diese Initialisierung eine einfache
Zuweisung im Rumpf der Klassendefinition.
Als Beispiel soll unsere Jukebox sich merken, wie oft jeder Tirel gespielt
wurde. Diese Anzahl könnte man gut als Instanz-Variable des
Song
-Objektes realisieren. Wenn ein Song
gespielt wird, wird der Wert in dieser Instanz um eins erhöht. Wenn wir nun
aber wissen wollen, wieviele Songs überhaupt gespielt wurden? Wir könnten
alle Song
-Objekte durchsuchen und ihre
Zähler aufaddieren, wir könnten auch die Exkommunizierung aus der
Kirche des guten Designs riskieren und eine globale Variable benutzen.
Stattdessen nehmen wir eine Klassenvariable.
class Song @@plays = 0 def initialize(name, artist, duration) @name = name @artist = artist @duration = duration @plays = 0 end def play @plays += 1 @@plays += 1 "This song: [email protected] plays. Total #@@plays plays." end end |
Song#play
einen String
zurückgeben, der die Anzahl angibt, wie oft dieser Song gespielt wurde,
zusammen mit der Gesamtanzahl für alle Songs. Wir können das
ganz einfach testen.
s1 = Song.new("Song1", "Artist1", 234) # test songs.. |
||
s2 = Song.new("Song2", "Artist2", 345) |
||
s1.play |
» | "This song: 1 plays. Total 1 plays." |
s2.play |
» | "This song: 1 plays. Total 2 plays." |
s1.play |
» | "This song: 2 plays. Total 3 plays." |
s1.play |
» | "This song: 3 plays. Total 4 plays." |
new
-Methode erzeugt ein neues Song
-Objekt, ist
aber selbst nicht mit einem bestimmten Song verbunden.
aSong = Song.new(....) |
File
eine
offene Datei des darunterliegenden Datei-Systems. Darüber hinaus
unterstützt die Klasse File
mehrere Methoden, um
Dateien zu manipulieren, die nicht geöffnet sind und deshalb auch kein
File
-Objekt haben. Wenn man eine Datei
löschen will, benutzt man die Klassen-Methode
File.delete
und gibt ihr den Namen mit.
File.delete("doomedFile") |
class Example def instMeth # Instanz-Methode end def Example.classMeth # Klassen-Methode end end |
SongList
definieren, die prüft,
ob ein bestimmter Song eine Grenzdauer überschreitet. Zum Setzen dieser
Grenze benutzen wir eine Klassen-Konstante, das ist eine einfache Konstante
(schon vergessen? Die fangen mit einem Großbuchstaben an), die im
Klassen-Rumpf definiert wird.
class SongList |
||
MaxTime = 5*60 # 5 minutes |
||
|
||
def SongList.isTooLong(aSong) |
||
return aSong.duration > MaxTime |
||
end |
||
end |
||
song1 = Song.new("Bicylops", "Fleck", 260) |
||
SongList.isTooLong(song1) |
» | false |
song2 = Song.new("The Calling", "Santana", 468) |
||
SongList.isTooLong(song2) |
» | true |
Logger.create
.
Und wir werden sicherstellen, das immer nur höchstens ein Objekt
erzeugt wird.
class Logger private_class_method :new @@logger = nil def Logger.create @@logger = new unless @@logger @@logger end end |
Logger
s Methode new
privat machen,
verhindern wir, dass jemand mit dem üblichen Konstruktor ein Log-Objekt erzeugt.
Anstelle dessen stellen wir eine neue Klassen-Methode zur Verfügung:
Logger.create
. Diese benutzt die
Klassen-Variable @@logger
, die eine Referenz auf eine einzelne (die
einzige) Instanz von logger hält und diese Instanz jedesmal zurückgibt, wenn
sie aufgerufen wird. [Achtung: Die Implemantation von
Singeletons, die wir hier vorstellen, ist nicht thread-sicher; wenn
mehrere Threads am laufen sind, ist es möglich, mehrere Log-Objekte
zu erzeugen. Statt selber Thread-Sicherheit zu erzeugen, würden
wir wenn nötig eher das Singleton
-Mixin
verwenden, das es zu Ruby dazugibt und das auf Seite 472 beschrieben
ist..] Wir können dies prüfen, indem wir uns die
von der Methode zurückgegebenen Objekt-Identifikatoren ansehen.
Logger.create.id |
» | 537684700 |
Logger.create.id |
» | 537684700 |
Shape
dienen,
die ein regelmäßiges Polygon repräsentiert. Instanzen von Shape
werden erzeugt, indem man dem Konstruktor die benötigte Seiten-Anzahl und
den Durchmesser mitgibt.
class Shape def initialize(numSides, perimeter) # ... end end |
Shape
hinzufügen.
class Shape def Shape.triangle(sideLength) Shape.new(3, sideLength*3) end def Shape.square(sideLength) Shape.new(4, sideLength*4) end end |
initialize
, die ist immer
private).
public
, protected
und private
.
Jede dieser Funktionen kann auf zwei verschiedene Arten benutzt werden.
Wenn man sie ohne Argumente benutzt, setzen diese Funktionen die
Zugriffskontrolle auf danach definierten Methoden. Dies ist wahrscheinlich
für C++ oder JavaProgrammierer ganz bekannt, wo man Schlüsselwörter wie
public
benutzt, um denselben Effekt zu erzielen.
class MyClass def method1 # default ist 'public' #... end protected # die folgende Methode ist 'protected' def method2 # die ist 'protected' #... end private # die folgende Methode ist 'private' def method3 # die ist 'private' #... end public # die folgende Methode ist 'public' def method4 # und die ist wieder 'public' #... end end |
class MyClass def method1 end # ... und so weiter public :method1, :method4 protected :method2 private :method3 end |
initialize
-Methode einer Klasse ist automatisch
private.
Jetzt ist es Zeit für ein paar Beispiele. Vielleicht sollten wir ein
Kontensystem entwickeln, wo jeder Schuldner ein nur ihm zugeordnetes
Guthaben hat. Weil wir diese Zuordnung schützen wollen, machen wir die
Methoden zum Setzen der Guthaben und Schulden private, und das
externe Interface definieren wir mit Mitteln der Transaktion.
class Accounts private def debit(account, amount) account.balance -= amount end def credit(account, amount) account.balance += amount end public #... def transferToSavings(amount) debit(@checking, amount) credit(@savings, amount) end #... end |
Account
-Objekten
erlauben, ihr Guthaben zu vergleichen, aber gleichzeitig möchten wir das
vor dem Rest der Welt geheimhalten (vielleicht wollen wir das dort in einer
schöneren Form ausdrücken).
class Account attr_reader :balance # Zugriffs- Methode 'balance' protected :balance # wird hier protected gemacht def greaterBalanceThan(other) return @balance > other.balance end end |
balance
protected ist, ist es nur
innerhalb von Account
-Objekten verfügbar.
person = "Tim" |
||
person.id |
» | 537684980 |
person.type |
» | String |
person |
» | "Tim" |
person
geschoben.
Ein kurzer Check zeigt, dass die Variable tatsächlich die
Rolle eines Strings angenommen hat, mit einer Objekt-Id, einem
Typ und einem Wert.
Also ist eine Variable ein Objekt?
In Ruby lautet die Antwort ``Nein''. Eine Variable ist einfach nur eine
Referenz auf ein Objekt. Objekte schwimmen in einem großen Topf
umher (meistens der Heap (der Übersetzer: der köchelt irgendwo im
computer rum)) und die Variablen zeigen auf sie.
Jetzt machen wir das Beispiel etwas komplizierter.
person1 = "Tim" |
||
person2 = person1 |
||
|
||
person1[0] = 'J' |
||
|
||
person1 |
» | "Jim" |
person2 |
» | "Jim" |
person1
, aber beide person1
und person2
änderten sich von ``Tim'' zu ``Jim.''
Das kommt alles daher, dass Variablen Referenzen auf Objekte
halten, nicht die Objekte selber. Die Zuweisung von person1
zu person2
erzeugt gar kein neues Objekt; sie kopiert einfach
nur person1
s Objekt-Referenz auf person2
, so dass beide
person1
und person2
auf das selbe Objekt zeigen. Wir
zeigen dies in Figur 3.1 auf Seite 33.
Die Zuweisung von Alias-Objekten gibt einem
möglicherweise mehrere Variablen, die aufs selbe Objekt zeigen.
Kann das nicht zu Problemen führen?
Ja, es kann, aber nicht so oft, wie man denkt (Objekte in Java etwa
funktionieren nach genau dem gleichen Schema). Im Speziellen kann man
solch ein Aliasing vermeiden, indem man wie in dem Beispiel aus Fugur 3.1
die dup
-Methode von String
benutzt,
die ein neues String
-Objekt erzeugt mit dem
identischen Inhalt.
person1 = "Tim" |
||
person2 = person1.dup |
||
person1[0] = "J" |
||
person1 |
» | "Jim" |
person2 |
» | "Tim" |
TypeError
-Exception auslösen.
person1 = "Tim" person2 = person1 person1.freeze # verhindert Modifikationen an dem Objekt person2[0] = "J" |
prog.rb:4:in `=': can't modify frozen string (TypeError) from prog.rb:4 |