|
Du bist hier:
Hinterkattentuffel -
Ruby -
Klassen definieren Prev: Funktionen definieren |
|
Ruby ist voll objektorientiert. Gelegentlich versteckt Ruby diese Eigenschaft, aber sie ist inherent vorhanden. Wer objektorientiert programmiert, definiert zunächst Klassen mit dazugehörigen Attributen und Methoden. Klassen sind Vorlagen für zu erzeugende Objekte, Attribute sind Behälter für die Daten eines Objekts und Methoden sind eine Möglichkeit, dem Objekt mitzuteilen, was es tun soll. Im fertigen Programm "reden" am Ende nur noch Objekte miteinander, d. h. Objekte sagen Objekten, was sie tun sollen. Eine triviale Klasse, d. h. eine die nicht besonders viel kann, zu erzeugen ist sehr einfach: IRB> class IchBinEineDummeKlasse IRB> end => nil Ebenfalls leicht: Eine Klasse die das gleiche kann, wie eine andere Klasse, aber nicht einfach nur eine Kopie ist. Soetwas nennt sich Unterklasse. Eine Unterklasse erbt dafür vorgesehene Methoden und Attribute ihrer Oberklasse und kann diese ändern oder auch zusätzliche Methoden implementieren. Ohne, dass wir es gemerkt haben, hat unsere dumme Klasse von eben einige Methoden von einer Klasse Object geerbt. Welche das sind, kann man sich über IchBinEineDummeKlasse.new.methods anzeigen lassen. Woher ich so sicher bin, dass diese Methoden von Object stammen? Das sagt mir IchBinEineDummeKlasse.superclass. Interessanter wird es, wenn man der eigenen Klasse zusätzliche Methoden und Attribute gibt. Ein einfaches Beispielprogramm zeigt, wie das konkret funktioniert: In walker.rb._0.1.txt ist Walker eine Klasse. Walker.new wäre eine Instanz (ein Exemplar) der Klasse. Die Klassenmethode new stammt von der Oberklasse Object. Sie erzeugt eine Instanz und ruft für diese Instanz automatisch die Instanzmethode initialize auf. Dort werden typischerweise die Instanzvariablen (=Attribute) initialisiert. Die dort nicht aufgeführten Variablen, also z. B. @direction, sind zunächst mit nil vorbelegt. Je nach Geltungsbereich einer Variablen unterscheidet man Klassenvariable, Klassenistanzvariable und Instanzvariable. Verschiedene Instanzen der gleichen Klasse können unterschiedliche Werte in ihren Instanzvariablen vorhalten. Der Wert einer Klasseninstanzvariable ist dagegen für alle Instanzen einer Klasse gleich. Der Wert einer Klassenvariable ist für alle Instanzen der Klasse und ihrer Unterklassen gleich. Dass ein Walker stets 'W' und ein RandomWalker stets 'R' schreibt, liegt an der Klasseninstanzvariable @char. Die Ausgabebreite @@width ist dagegen global für alle Instanzen gleich. Hier ein Durchlauf des Programms als Screenshot: walker.jpg. Der Code dazu steht am Ende der Datei: if $0 == __FILE__ Walker.new.run randomWalker = RandomWalker.new randomWalker.run Walker.setWidth 20 Walker.new.run RandomWalker.new.run randomWalker.run end Jedes new erzeugt ein Objekt, wir haben hier also Output von vier Objekten. Der letzte Output stammt von einem Objekt, dass wir weiter oben in die Variable randomWorker abgelegt hatten.
Wie bei Variablen, so gibt es auch bei den Methoden Geltungsbereiche. Klassenmethoden wie new braucht man in der Praxis nur selten. Der Regelweg ist, mit new ein Objekt zu erzeugen und diesem dann Methoden zu schicken. Solche Instanzmethoden definieren die Objektschnittstelle, gewissermaßen das Verständigungs-Repertoire des Objekts. Diese Schnittstelle wird von Ober- zu Unterklasse vererbt. Im Beispiel überschreibt die Unterklasse RandomWalker eine Methode der Oberklasse, so dass Instanzen von RandomWalker sich etwas anders verhalten als Instanzen von Walker. Das Idiom if $0 == __FILE__ findet man recht häufig. Es bedeutet, dass der Code ausgeführt wird, wenn erste Parameter für den Ruby-Interpreter gleich der aktuellen Datei ist. Man kann den Code nämlich auch auf mehrere Dateien aufteilen. Wir können z. B. die Datei walker.rb aus einer Datei runwalker.rb heraus aufrufen. Dazu legt man die Dateien in das gleiche Verzeichnis und verwendet die Anweisung require um die Abhängigkeit darzustellen: runwalker.rb.txt. In größeren Projekten spendiert man meist jeder Klasse eine eigene Datei. Später werden wir lernen, wie man diese Dateien zu Installationspaketen zusammenpackt. Dann wird __FILE__ enorm praktisch. Etwa, wenn man von einer Programmdatei aus Ressourcen laden möchte, aber den Installationspfad nicht kennt. Ich verwende diese Methode, um die Icons einer Symbolleiste zu laden. Eine Warnung am Schluss: Es ist möglich, Klassen zu überschreiben. Das gilt auch für Systemklassen. Zwar ist das in vielen Fällen durchaus hilfreich, aber man kann sich mit ungeschickten Überschreibungen natürlich auch sein Programm zerschießen. Da wir jetzt Klassen kennen, wagen wir den nächsten Schritt: Metaklassen. |
| Next: Metaprogrammierung |