Ruby-Programmierung: Klassen
Zurück zum Inhaltsverzeichnis.
Typischerweise erfolgt eine Einführung in objektorientierte Programmierung mittels einem Beispiels anhand dessen man die sowohl die Eigenarten der Objekte, als auch deren Umsetzung in einer bestimmten Sprache kennenlernen soll. Dieses Kapitel folgt diesem Konzept mit einer kleinen Abwandlung: Vor einem solchen Beispiel soll Objektorientierung motiviert werden, denn Sie könnten die berichtigte Frage stellen, wozu ein solches Konzept nötig ist, wenn Sie mit den in den Grundlagen beschriebenen Konzepten vollständig programmieren können.
Motivation
BearbeitenDiese Einleitung dient vor allem Neulingen beim Verständnis, wenn Sie bereits eine andere Objektorientierte Sprache beherrschen können Sie diesen Abschnitt überspringen.
Kehren wir einmal zurück zu dem Beispiel aus den Grundlagen mit dem Kassenbon. Dieses zugegebenermaßen nicht besonders ausgefeilte Beispiel, soll uns als Grundlage dienen. Das Produkt hatte einen Namen und einen Preis. Diese Daten korrelieren offensichtlich für ein einzelnes Produkt, aber zwischen den Produkten besteht kein Zusammenhang. Realisiert wurde dies durch die Verwendung eines Hashes. Stellen Sie sich jedoch einmal vor, dass mehr Daten zusammenhängen würden. Sie müssten dann innerhalb des Hashes wieder ein Hash oder ein Array verwenden und sich zusätzlich der Struktur ihres Datentyps an jeder Stelle im Programm bewusst sein.
Kapselung ist die erste wichtige Eigenschaft von Objekten, es bedeutet dass innerhalb eines Objektes Daten vorliegen, die über dieses objekt miteinander in Verbindung stehen. In unserem Beispiel also Name und Preis. In den meisten Umsetzungen von Objekten werden jedoch nicht nur Daten gekapselt, sondern auch Methoden. Dies hat den Vorteil, dass ein String#+
etwas völlig anderes bedeuten kann als Array#+
, weil es sich um Methoden des Objektes String bzw. Array handelt.
Nun zurück zur Praxis:
Klassen
BearbeitenIn der Motivation wurde das Wort Klasse nicht erwähnt, weil es für das Konzept von Objekten nicht wichtig ist. Es gibt objektorientierte Sprachen, die völlig ohne ein Klassensystem auskommen. Klassen sind in erster Linie ein Bauplan bzw. eine Beschreibung des Objekts, während eine konkrete Umsetzung oder auch Instanz genannt ein Objekt ist.
Klassennamen müssen gemäß Sprachdefinition mit Großbuchstaben anfangen. Es ist üblich, Klassennamen im CamelCase zu schreiben. Man verzichtet auf die Verwendung von Unterstrichen und benutzt stattdessen Großbuchstaben jeweils am Anfang eines neuen Wortes oder Wortteils, z.B. Drink
oder LongDrink
.
Die Definition einer Klasse erfolgt mit dem Schlüsselwort class
und wird wie üblich mit end
beendet. Eine minimale Klasse entspricht folgendem Schema:
class KlassenName
def initialize
end
end
Diese Klasse hat einen Konstruktor KlassenName.new
. Dieser ist dafür verantwortlich ein neues Object zu erstellen. Dafür sind zwei Schritte notwendig: Zum einen das Anfordern des Speicherplatzes vom Betriebssystem und zum anderen die spezielle Initialisierung des Objekts auf seinen Anfangszustand. Da dies immer der Fall ist, muss nicht jedesmal KlassenName.new
implementiert werden, sondern diese Methode ruft ihrerseits die oben geschriebene Methode initialize
auf.
Instanzmethoden und -variablen
BearbeitenDie oben beschriebene Klasse erfüllt nur begrenzten Sinn, da man zwar von ihr Objekte erzeugen kann, diese aber nichts können. Möchte man also Daten und Funktionalität hinzufügen, so tut man dies mit Instanzmethoden und -variablen. Diese beziehen sich auf ein konkretes Objekt, d.h. dass sich Instanzvariablen also innerhalb Objekten der selben Klasse unterscheiden können und Instanzmethoden erst nach Konstruktion eines Objektes aufgerufen werden können.
Die erste Instanzmethoden ist initialize
. Sie werden ganz normal mit def
eingeleitet und mit end
abgeschlossen. Der einzige Unterschied zu normalen Methoden besteht also darin, dass sie in einer Klassendefinition stehen.
Instanzvariablen beginen mit einem @
-Zeichen. Bedienen wir uns wieder eines Beispiels: Angenommen Sie entwickeln ein Text-RPG und wollen nun ein Schwert hinzufügen. Es hat eine Beschreibung und einen Namen und man kann damit zuhauen.
class Sword
def initialize(name,description)
@name = name
@description = description
end
def hit
puts "The mighty sword #{@name} hits!"
end
end
Alle Instanzvariablen sind nicht von außen sichtbar, dass bedeutet im vorrangegangenen Beispiel weiß zwar das Objekt die Beschreibung des Schwertes, jedoch kann man nicht von außen darauf zugreifen. Alle Veränderungen und Ausgaben des Objektes erfolgen über Instanzmethoden! Es ist Möglich diese manuell zu erstellen indem man zum Beispiel folgende Methoden hinzufügt:
class Sword
def name
@name
end
def name=(name)
@name = name
end
end
Oder die Methoden attr_reader
, attr_writer
oder attr_accessor
verwenden um jeweils das Lesen, Schreiben oder beides eines Symbols zu ermöglichen. Im Obigen Fall heißt das also:
class Sword
attr_accessor :name, :description
end
Attribute von Instanzmethoden
BearbeitenWie oben erläutert handelt es sich bei den Instanzvariablen grundsätzlich um private Eigenschaften des Objektes, das bedeutet das niemand direkt auf sie zugreifen kann, sondern dies durch die Verwendung von Methoden geschieht (wenn überhaupt). Auch Instanzmethoden kann man auf diese weise vor einem Zugriff schützen. Der Standard ist public
, dass heißt Instanzmethoden können von überall im Programm aufgerufen werden. Dazu gibt es private
, d.h. die Methode kann nur innerhalb des Objektes aufgerufen werden und protected
, d.h. die Methode kann innerhalb aller Objekte der gleichen Klasse und deren Erben aufgerufen werden.
Klassenmethoden und -variablen
BearbeitenKlassenmethoden wie zum Beispiel File.delete
können ohne die Erzeugung eines Objekts aufgerufen werden. Beim Beispiel des Files ist die Notwendigkeit offensichtlich: die Erzeugung eines Fileobjekts würde das Öffnen der Datei voraussetzen, jedoch können geöffnete Dateien nichtmehr gelöscht werden. Die häufigste Verwendung einer Klassenmethode ist der Konstruktor .new
.
Für die Definition von Klassenmethoden existieren zwei mögliche Schreibweisen, die man Abhängig von der Anzahl zu definierender Klassenmethoden verwendet werden sollte. Sind wenige Methoden zu definieren, dann ist def self.method
leichter zu lesen, während bei vielen Klassenmethoden die zweite Möglichkeit weniger Code wiederholt.
class Example
def self.hello(str)
puts "Hello #{ str }!"
end
class << self
def hello2(str)
puts "And hello #{ str }!"
end
end
end
Example.hello("World")
Example.hello2("Wikipedia")
Klassenvariablen beginnen mit @@
. In jeder Objektinstanz der Klasse haben sie den gleichen Wert und sind daher quasi globale Variablen im Kontext einer Klasse. Aufgrund des seltsamen Verhalten von Klassenvariablen bei der Verwendung von Vererbung, sollte man Sie entweder durch globale Variablen ersetzen, oder durch Umstrukturierung des Programms überflüssig machen.