Author: et, zuletzt bearbeitet von progman @ 2003/03/29 20:53:02
Bitte beachten Sie, dass die Tutorialkapitel zusammenhängen. Wenn sie direkt auf ein Kapitel verlinkt wurden müssen Sie gegebenenfalls die vorherigen Kapitel auch lesen. Achten Sie beim lesen darauf, dass Sie kein Kapitel überspringen.
Klassen und Objekte
- Was bedeutet 'OOP'?
- Methoden
- Eigenschaften
- Klassen und Vererbung
- PHP und OOP
- Vererbung in PHP und typische Fallen
1. Was bedeutet 'OOP'?
OOP steht für Objekt-Orientierte Programmierung. Was bedeutet das? Nun, bisher haben wir in unseren Variablen nur Werte gespeichert, Funktionen aufgerufen und Rückgabewerte von Funktionen in Variablen gespeichert. Im Grunde besteht das Programm zur Zeit aus 3 Teilen: Aus Anweisungen (mittels Funktionen und Operatoren), aus Variablen und aus Werten. Die Programmierweise, die wir kennengelernt haben, nennt sich imperative Programmierung, bzw. - dank des Fehlens einer ``GOTO''-Anweisung - strukturierte Programmierung.
Wieso sollten wir etwas daran ändern? Nun, zum einen ist diese Programmierweise mehr an Computern als an der ``Real World'' orientiert. Ein Problem aus dem wirklichen Leben muss meist erst ``übersetzt'' werden, damit man es lösen kann. Zum anderen ist Code, der objekt-orientiert geschrieben ist, eher wiederverwertbar als nicht-oo Code. Wieso? [Beispiel aus ``Programming Ruby'' von David Thomas, Andrew Hunt] Nun, angenommen wir haben eine Funktionensammlung für Mathematik / Trigonometrie, die wir brauchen um geometrische Probleme zu lösen. Diese enthält -natürlich- auch eine Funktion namens ``sin'', um den Sinus eines Winkels zu berechnen. Eine andere Funktionensammlung beschäftigt sich mit Gut und Böse und hat auch eine Funktion namens ``sin'', die benutzt wird um die Anzahl der Sünden eines Wesens herauszufinden. Ein Wesen mit 0 Sünden muss ein Engel sein. Getrennt funktionieren die beiden natürlich super, aber sobald dir jemand die Aufgabe stellt, zu berechnen, wieviele Engel auf eine Nadelspitze passen, hast du ein echtes Problem. Was tun wir dagegen? Ich habe es bereits vorweggenommen - wir programmieren objekt-orientiert. Anstatt einer Funktion ``sin'', die uns die Anzahl Sünden eines Wesens zurückliefert, behandeln wir die Wesen als Objekte und fragen sie, wieviele Sünden sie begangen haben. (Jedoch steht es den Objekten wie im wirklichen Leben frei zu lügen, so dass wir vielleicht ein ``zentrales Sündenregister''-Objekt einrichten sollten. ;))
Ein objektorientiertes Programm besteht aus Objekten, die miteinander kommunizieren. Kleines Beispiel: Nehmen wir an, ich hätte ein Meerschweinchen namens Washu mit telepathischen Kräften, das mich direkt beeinflussen könnte, würde es - das Objekt ``Washu'', sobald es hungrig wird, eine Nachricht mit dem Inhalt ``hole Futter'' an mich - das Objekt ``et'' senden. Dann verarbeite ich diese Nachricht, indem ich Futter hole (was natürlich auch komplett objekt-orientiert abläuft, ich schicke der Tür zur Abstellkammer, in der das Futter ist, die Nachricht ``öffne dich!'' und so weiter ;)) und gebe dann Washu die Antwort auf seine Nachricht zurück, nämlich das angeforderte Futter.
Soweit, so gut. Aber wie läuft die Kommunikation zwischen den Objekten ab? Die Antwort lautet: Mit Methoden.
2. Methoden
Methoden bestimmen, was für Nachrichten ein Objekt empfangen kann und wie es mit den Nachrichten umgeht. In dem genannten Beispiel mit dem Meerschweinchen wäre ``hole Futter'' eine Methode des Objektes ``et'', ``öffne dich!'' wäre eine Methode des Objektes ``Abstellkammer-Tür'' und so weiter. Nachrichten müssen aber nicht so simpel sein: Wenn Washu wählerisch ist und Heu als Futter will, wird er die Nachricht ``hole Futter Heu'' an mich senden (Er hält mich für bescheuert und denkt, dass mich zusätzliche Worte verwirren würden.) Und schon haben wir unser erstes Problem: Was für eine Methode soll diese Nachricht nun verarbeiten? Es könnte ja sein, dass es eine Methode namens ``hole'' gibt. Soll dann diese Methode mit den Zusatzinfos ``Futter'' und ``Heu'' aufgerufen werden, oder die Methode ``hole Futter'' mit der Zusatzinfo ``Heu''? Man könnte jetzt ein obskures Trennzeichen vereinbaren, in PHP jedoch gelten für Methoden die gleichen Regeln wie für Funktionen, sprich: Der Name besteht aus Buchstaben, Zahlen und dem Underscore, beginnend mit einem Buchstaben oder einem Underscore (``_''), die Zusatzinfos werden mit einer (eventuell auch leeren) Liste von Parametern übergeben. Es gibt die Übereinkunft, dass Methoden mit einem Kleinbuchstaben (oder einem Underscore, darauf komme ich später zurück) beginnen und jedes neue Wort durch einen Grossbuchstaben gekennzeichnet ist, zum Beispiel würde die Methode, die Washu aufruft, ``holeFutter'' heißen.
Bestehen Objekte nur aus Methoden? Nun, wenn das so wäre, könnte Washu sich nicht merken, dass er gerade Futter bekommen hat und jetzt satt ist, und würde mich dauernd Futter holen schicken - nicht gerade angenehm für mich. Objekte brauchen also ein ``Gedächtnis'', einen Zustand. Dieser Zustand setzt sich aus den Eigenschaften des Objektes zusammen.
3. Eigenschaften
Eigenschaften sind wie gesagt das Gedächtnis eines Objekts. Washu hätte z.B. eine Eigenschaft ``hunger'' (für Eigenschaften gelten die gleichen Namensgebungskonventionen wie für Methoden, deswegen schreibe ich es gleich klein), die festhält, wie hungrig er ist. Die Tür hätte eine Eigenschaft, in der gespeichert ist, ob sie offen ist, und so weiter.
Wichtig ist: Eigenschaften sind INTERNE Sachen, man liest sie nicht von außen aus, und man überschreibt sie niemals von außen, auch wenn es - wie in PHP - möglich ist. Allein die Methoden eines Objektes bilden die Schnittstelle zur Außenwelt, nur über sie kommunizieren Objekte. Den Grund dafür werden wir sehen, wenn wir zum praktischen Teil kommen - da werde ich vorführen, was für ein Murks da passieren kann.
Wie wir gesehen haben, sind Objekte eine Kombination aus Methoden und einem Zustand. Die Methoden und den Anfangszustand für jedes Objekt einzeln zu definieren, ist ein bisschen aufwendig, wenn wir uns ansehen, wieviele Meerschweinchen, Türen und Menschen es auf der Erde gibt. Wir brauchen also ein Grundgerüst für Objekte, welches die Methoden und eventuell auch Standardwerte für die Eigenschaften definiert. Diese Grundgerüste nennen sich Klassen.
4. Klassen und Vererbung
Jedes Objekt hat genau eine Klasse. Washu hätte die Klasse ``Meerschweinchen'', die Tür zur Abstellkammer hätte die Klasse ``Tür'' und ich hätte die Klasse ``Mensch''. Anstelle dessen sagen wir auch: Washu ist eine Instanz der Klasse Meerschweinchen. Für Klassen gelten die gleichen Namenskonventionen wie für Methoden, jedoch fangen sie mit einem Großbuchstaben an. Instanzen einer Klasse haben immer die gleichen Methoden, verstehen also die gleiche Art von Nachrichten, aber sie haben nicht zwingend den gleichen Zustand, das heißt zwei verschiedene Instanzen der gleichen Klasse können auf die gleiche Nachricht durchaus unterschiedlich reagieren.
Es gibt ein weiteres praktisches Prinzip, das sich Vererbung nennt. Wenn wir eine Klasse erweitern wollen, müssen wir sie nicht komplett neuschreiben. Wenn wir Meerschweinchen haben wollen, die schwanger werden können, wäre es praktisch, wenn wir zwischen männlichen und weiblichen Meerschweinchen unterscheiden könnten, denn sonst könnte es uns passieren, dass auf einmal die männlichen Meerschweinchen schwanger werden - was die weiblichen zwar lustig finden würden, aber nicht wirklich Sinn der Sache war. Also erstellen wir zwei Unterklassen von ``Meerschweinchen'' namens ``Meerschweinchen_m'' und ``Meerschweinchen_w'' und definieren in ihnen nur die Methoden zum schwängern und schwanger werden, und die Eigenschaften die dafür nötig sind - es wäre ja dumm, wenn das Weibchen nicht wüsste, dass es schwanger ist. Instanzen der Klassen ``Meerschweinchen_m'' und ``Meerschweinchen_w'' können dann trotzdem alle Nachrichten verarbeiten, die eine Instanz der Klasse ``Meerschweinchen'' verarbeiten kann (es sei denn, wir haben etwas verbockt).
Das war jetzt nur ein kleines bisschen Theorie über OOP, und auch nur möglichst ``einsteigerfreundlich'' geschrieben. Kommen wir jetzt zu dem Teil, wegen dem der ganze Text hier geschrieben wurde...
5. PHP und OOP
Bevor wir Objekte nutzen können, müssen wir eine Klasse definieren. Dies geschieht in PHP mit dem Keyword ``class''. Eigenschaften deklariert man mittels ``var'', sie können zwar auch zur Laufzeit erstellt werden, dies ist aber wesentlich unübersichtlicher. Methoden werden genau wie Funktionen deklariert, nur innerhalb einer Klasse. In PHP sieht das so aus:
<?php
// Wir deklarieren eine Klasse namens ``Meerschweinchen''
class Meerschweinchen {
// Wir deklarieren ein paar Eigenschaften
// und weisen ihnen einen Startwert zu
var $hunger = 0;
var $laenge = 10;
// Wir deklarieren ein paar Methoden
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen.\r\n";
return $message;
}
function getLaenge()
{
return $this->laenge;
}
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)$neueLaenge;
return $this->laenge;
}
}
?>
Dies lässt sich mit unserem - hoffentlich jetzt vorhandenen - Vorwissen leicht lesen. Die Klasse ``Meerschweinchen'' hat die Methoden ``wasBistDu'', ``getLaenge'' und ``setLaenge''. Erstere gibt uns Informationen über das Meerschweinchen, die zweite sagt uns, wie lang die Zähne des Meerschweinchens sind (mit irgendwas müssen die ja angeben), und die dritte lässt uns die Länge der Zähne setzen. $this zeigt dabei auf das aktuelle Objekt, mit $this->methode() greifen wir auf eine Methode des aktuellen Objektes zu, und mit $this->eigenschaft auf - wer hätte es gedacht - eine Eigenschaft. Mit -> greifen wir auch auf Methoden von anderen Objekten zu, z.b. $tuer->oeffnen(). Wer fragt da, wie wir auf Eigenschaften von anderen Objekten zugreifen? Pfui! Ich sagte bereits, das macht man nicht, und wir werden bald sehen, wieso...
Jetzt haben wir eine Klasse. Woher bekommen wir nun ein Objekt? Dafür ist das Keyword new da. Mit $objekt = new Klasse; erzeugen wir eine Instanz der Klasse ``Klasse'' und weisen sie $objekt zu. Nun wollen wir jedem Meerschweinchen bei der Erstellung eine Länge der Zähne zuweisen. Das könnten wir natürlich mit etwas, das ähnlich aussieht wie
<?php
$Meerschweinchen1 = new Meerschweinchen;
$Meerschweinchen1->setLaenge(9);
$Meerschweinchen2 = new Meerschweinchen;
$Meerschweinchen2->setLaenge(11);
?>
machen, das ist jedoch ein bisschen umständlich. Dafür gibt es Konstruktoren, das sind Methoden, die bei dem Erzeugen des Objektes automatisch aufgerufen werden. In PHP4 ist ein Konstruktor eine Methode, die den gleichen Namen hat wie die Klasse, in der sie definiert wurde. Beispiel:
<?php
// Durch diesen Befehl sieht man den Quelltext, und nicht die HTML-
// Darstellung, so dass \r\n auch als Zeilenumbruch angezeigt wird
header('Content-Type: text/plain');
class Meerschweinchen {
var $hunger = 0;
// Default-Werte sind nicht zwingend nötig, hier wird die Länge im
// Konstruktor definiert
var $laenge;
// Dies ist der Konstruktor
function Meerschweinchen($laenge = 10)
{
$this->setLaenge($laenge);
}
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen.\r\n";
return $message;
}
function getLaenge()
{
return $this->laenge;
}
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)$neueLaenge;
return $this->laenge;
}
}
// Wir erzeugen ein paar Meerschweinchen - genauer gesagt, Instanzen der Klasse
// ``Meerschweinchen''
$Washu = new Meerschweinchen(11);
// Der Parameter des Konstruktors ist in unserem Fall optional
$Ebichu = new Meerschweinchen;
// Inspizieren wir die Meerschweinchen...
print $Washu->wasBistDu();
print $Ebichu->wasBistDu();
// produziert:
?>
Ich bin ein Meerschweinchen mit 11 Millimeter langen Zähnen. Ich bin ein Meerschweinchen mit 10 Millimeter langen Zähnen.
Natürlich können Konstruktoren auch zwingende oder mehr Argumente haben.
6. Vererbung in PHP und typische Fallen
Nun probieren wir mal Vererbung aus: Durch das Keyword extends drücken wir aus, dass wir eine Unterklasse erstellen wollen.
<?php
header('Content-Type: text/plain');
// Alte Klasse
class Meerschweinchen {
var $hunger = 0;
var $laenge;
function Meerschweinchen($laenge = 10)
{
$this->setLaenge($laenge);
}
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen.\r\n";
return $message;
}
function getLaenge()
{
return $this->laenge;
}
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)$neueLaenge;
return $this->laenge;
}
}
// Aus alt mach neu
class FarbigesMeerschweinchen extends Meerschweinchen {
// Wir müssen nur die neue Farbe als Eigenschaft definieren
var $farbe;
// Neuer Konstruktor
function FarbigesMeerschweinchen($laenge = 10, $farbe = 'braun')
{
$this->setLaenge($laenge);
// Das Meerschweinchen wird seine Farbe nicht wechseln,
// eine Methode ist also sinnlos.
$this->farbe = $farbe;
}
function getFarbe()
{
return $this->farbe;
}
}
$Washu = new FarbigesMeerschweinchen(11,'weiß');
$Ebichu = new FarbigesMeerschweinchen;
// Inspizieren wir die Meerschweinchen...
print $Washu->wasBistDu();
print $Washu->getFarbe()."\r\n";
print $Ebichu->wasBistDu();
print $Ebichu->getFarbe()."\r\n";
// produziert:
?>
Ich bin ein Meerschweinchen mit 11 Millimeter langen Zähnen. weiß Ich bin ein Meerschweinchen mit 10 Millimeter langen Zähnen. braun
Aber eigentlich wollen wir, dass die Methode ``wasBistDu'' auch die Farbe zurückgibt. Nun, wir können die Methode einfach überschreiben, allerdings sollte man dabei aufpassen. Ich führe es erst einmal so vor, wie man es nicht machen sollte, und mache dabei auch andere Sachen, die man nicht machen sollte, damit wir sehen, was dabei schieflaufen kann.
<?php
header('Content-Type: text/plain');
class Meerschweinchen {
var $hunger = 0;
var $laenge;
function Meerschweinchen($laenge = 10)
{
$this->setLaenge($laenge);
}
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen.\r\n";
return $message;
}
function getLaenge()
{
return $this->laenge;
}
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)$neueLaenge;
return $this->laenge;
}
}
class FarbigesMeerschweinchen extends Meerschweinchen {
var $farbe;
function FarbigesMeerschweinchen($laenge = 10, $farbe = 'braun')
{
$this->setLaenge($laenge);
$this->farbe = $farbe;
}
function getFarbe()
{
return $this->farbe;
}
// Überschreiben wir die Methode, wie man es nicht machen
// sollte...
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen. \r\nMein Fell ist "
.$this->farbe.".\r\n";
return $message;
}
}
$Washu = new FarbigesMeerschweinchen(15,'weiß');
$Ebichu = new FarbigesMeerschweinchen;
// Der Meerschweinchen-Zahnarzt findet, die Zähne von Washu
// sind zu lang, sie müssen gekürzt werden.
// Tun wir so, als wüssten wir nicht dass man die Finger von
// den Eigenschaften lässt und modifizieren wir sie mal direkt...
$Washu->laenge = 9;
// Inspizieren wir die Meerschweinchen...
print $Washu->wasBistDu();
print $Ebichu->wasBistDu();
// produziert:
?>
Ich bin ein Meerschweinchen mit 9 Millimeter langen Zähnen. Mein Fell ist weiß. Ich bin ein Meerschweinchen mit 10 Millimeter langen Zähnen. Mein Fell ist braun.
Also genau das, was wir wollten... Was zum Henker ist denn falsch dadran? Nun, angenommen uns gefällt nicht, wie die Klasse Meerschweinchen aufgebaut ist, wir wollen die Zahnlänge genauer speichern, und zusätzlich soll das Geburtsdatum jedes Mehrschweins in einer Eigenschaft gespeichert werden. Da wir die Methoden so anpassen, dass sie nach außen hin das Gleiche tun, und jede Unterklasse das können sollte, was die Oberklasse kann, sollte eigentlich alles funktionieren, wenn wir einfach die Klasse ``Meerschweinchen'' ändern - das ist schliesslich einer der Vorteile von OOP, dass alles gekapselt ist und getrennt voneinander nutzbar. Oder? Nun, probieren wir es einfach aus...
<?php
header('Content-Type: text/plain');
class Meerschweinchen {
var $geburtsdatum = 'Noch nicht geboren';
var $hunger = 0;
// Wir wollen die Länge nun als Zehntelmillimeter speichern
var $laenge = 100;
function Meerschweinchen($laenge = 10)
{
$this->setLaenge($laenge);
// date() gibt das aktuelle Datum anhand eines Formats
// zurück. d steht für Tag(day), m für Monat(month) und
// Y für Jahr(year)
$this->geburtsdatum = date('d.m.Y');
}
// Nun müssen wir aufpassen. Man soll die Klasse wie früher
// benutzen können, das heisst wir müssen die Länge in Millimetern
// zurückliefern. Wir haben sie in Zehntelmillimetern gespeichert,
// also teilen wir sie durch 10.
function getLaenge()
{
return $this->laenge/10;
}
// Das Gleiche gilt für das setzen der Länge, wir bekommen
// sie in Millimetern, also müssen wir sie mit 10 multiplizieren
// um sie in Zehntelmillimetern zu speichern.
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)($neueLaenge*10);
return $this->laenge;
}
// Da wir die Methode benutzen, um die Länge auszulesen,
// ändert sich hier nicht viel, außer dass wir zusätzlich
// das Geburtsdatum einfügen.
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen. \r\nIch wurde am "
.$this->geburtsdatum." geboren.\r\n";
return $message;
}
function getGeburtsdatum()
{
return $this->geburtsdatum;
}
}
// Unterklasse wie vorhin auch, keine Änderung
class FarbigesMeerschweinchen extends Meerschweinchen {
var $farbe;
function FarbigesMeerschweinchen($laenge = 10, $farbe = 'braun')
{
$this->setLaenge($laenge);
$this->farbe = $farbe;
}
function getFarbe()
{
return $this->farbe;
}
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen. \r\nMein Fell ist "
.$this->farbe.".\r\n";
return $message;
}
}
// Rest auch wie vorhin
$Washu = new FarbigesMeerschweinchen(15,'weiß');
$Ebichu = new FarbigesMeerschweinchen;
$Washu->laenge = 9;
// Inspizieren wir die Meerschweinchen...
print $Washu->wasBistDu();
print $Ebichu->wasBistDu();
// Noch das Geburtsdatum alleine kontrollieren:
print $Washu->getGeburtsdatum();
// produziert:
?>
Wir haben schön aufgepasst, dass die Klasse wie vorher benutzbar ist, also sollte nichts schiefgehen können. Sehen wir uns mal die Ausgabe an...
Ich bin ein Meerschweinchen mit 0.9 Millimeter langen Zähnen. Mein Fell ist weiß. Ich bin ein Meerschweinchen mit 10 Millimeter langen Zähnen. Mein Fell ist braun. Noch nicht geboren
Na Klasse. Nicht nur, dass wir von dem Geburtstag nichts sehen und er anscheinend nicht mal gesetzt ist, nein, wir haben auch noch Washu zum Krüppel gemacht. Mit 0.9 Millimetern wird er kaum noch ein Weibchen beeindrucken können.
Analysieren wir einmal, was passiert ist.
-
Da wir nicht die Methode ``setLaenge'' benutzt haben, sondern von außen direkt in die Eigenschaft geschrieben haben, sind aus den 9 Millimetern auf einmal 0.9 Millimeter geworden. Hätten wir die Methode benutzt, wäre der Wert intern mit 10 multipliziert worden und alles wäre normal geblieben.
-
Da der Konstruktor der Klasse ``FarbigesMeerschweinchen'' den Geburtstag nicht setzt, sondern nur der Konstruktor der Klasse ``Meerschweinchen'', aber nur der Konstruktor der Klasse ``FarbigesMeerschweinchen'' aufgerufen wird, wird der Geburtstag nicht gesetzt.
-
Die Methode ``wasBistDu'' wird in der Klasse ``FarbigesMeerschweinchen'' überschrieben, hat also keine Ahnung von der Existenz des Geburtstages.
Was machen wir dagegen? Nun, gegen den ersten Punkt: Wir greifen auf Eigenschaften von Objekten nur in den Klassen zu, in denen wir sie definieren. Für die beiden anderen Probleme gibt es die Möglichkeit, parent::methode() zu nutzen, wodurch die ensprechende Methode der Oberklasse (die, die der Klasse ihre Methoden und Eigenschaften vererbt hat) aufgerufen wird. Ich werde den Gebrauch von parent::methode() nun zeigen, um Washu wieder zu seinen 9 Millimetern zu verhelfen.
<?php
header('Content-Type: text/plain');
// Basisklasse wie vorhin
class Meerschweinchen {
var $geburtsdatum = 'Noch nicht geboren';
var $hunger = 0;
var $laenge = 100;
function Meerschweinchen($laenge = 10)
{
$this->setLaenge($laenge);
$this->geburtsdatum = date('d.m.Y');
}
function getLaenge()
{
return $this->laenge/10;
}
function setLaenge($neueLaenge)
{
if (!is_numeric($neueLaenge)) {
return false;
}
$this->laenge = (int)($neueLaenge*10);
return $this->laenge;
}
function wasBistDu()
{
$message = "Ich bin ein Meerschweinchen mit ".$this->getLaenge()
." Millimeter langen Zähnen. \r\nIch wurde am "
.$this->geburtsdatum." geboren.\r\n";
return $message;
}
function getGeburtsdatum()
{
return $this->geburtsdatum;
}
}
// Nun machen wir es besser, so dass es keine Probleme
// gibt wenn die Klasse ``Meerschweinchen'' wieder geändert wird.
class FarbigesMeerschweinchen extends Meerschweinchen {
var $farbe;
function FarbigesMeerschweinchen($laenge = 10, $farbe = 'braun')
{
// Hier kommt zum ersten mal ``parent::'' zum Einsatz.
// Wir rufen den Konstruktor der Klasse ``Meerschweinchen'',
// von der wir diese Klasse abgeleitet haben, auf.
// Dieser setzt dann die Länge und macht was immer er
// noch machen will - in diesem Fall den Geburtstag
// setzen.
parent::Meerschweinchen($laenge);
$this->farbe = $farbe;
}
function getFarbe()
{
return $this->farbe;
}
function wasBistDu()
{
// Anstatt die komplette Nachricht neuzuschreiben, holen
// wir uns die Nachricht von der Oberklasse und hängen unsere
// an.
$message = parent::wasBistDu();
$message.= "Mein Fell ist ".$this->getFarbe().".\r\n";
return $message;
}
}
$Washu = new FarbigesMeerschweinchen(15,'weiß');
$Ebichu = new FarbigesMeerschweinchen;
// Wir wissen jetzt, dass der Zahnarzt besser die Methode benutzen sollte.
$Washu->setLaenge(9);
// Inspizieren wir die Meerschweinchen...
print $Washu->wasBistDu();
print $Ebichu->wasBistDu();
// Noch das Geburtsdatum alleine kontrollieren:
print $Washu->getGeburtsdatum();
// produziert:
?>
Ich bin ein Meerschweinchen mit 9 Millimeter langen Zähnen. Ich wurde am 03.01.2003 geboren. Mein Fell ist weiß. Ich bin ein Meerschweinchen mit 10 Millimeter langen Zähnen. Ich wurde am 03.01.2003 geboren. Mein Fell ist braun. 03.01.2003
Sehr fein, genau das, was wir haben wollten. Natürlich werdet ihr andere Daten rausbekommen, wenn eure Systemuhr nicht falschgeht, aber das macht euch denke ich nichts aus :). Wer nicht glaubt, dass das auch mit der alten Klasse ``Meerschweinchen'' funktioniert, darf es gerne ausprobieren, vorführen werde ich es nicht.
Etwas letztes gibt es noch zu Klassen zu sagen: Mit Klassenname::methode() kann man auf Methoden von Klassen zugreifen, ohne eine Instanz der Klasse zu haben. Dies ist nützlich, um Funktionensammlungen, wie die erwähnten Sammlungen am Anfang, nebeneinander laufen zu haben - zwischen Math::sin() und GoodEvil::sin() gibt es keine Konflikte. Jedoch hat eine so aufgerufene Methode logischerweise keinen Zugriff auf ein $this, also auch nicht auf Eigenschaften oder andere Methoden derselben Klasse - es sei denn, sie rufen sie wieder mit Klassenname::methode() auf. Sie sind quasi ganz gewöhnliche Funktionen. Codebeispiel:
<?php
// Es ist nicht nötig zu verstehen, was strtr macht, um dieses
// Beispiel zu verstehen. Wenn ihr es verstehen wollt,
// http://www.php.net/strtr
class Text {
function l33tify($text)
{
return strtr($text,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'4bCd3fGh|Jk1Mn0PqR57UvWxYZ4ßcD3fGh|jKlmNoPQr57UvWxYz'
);
}
function rot1($text)
{
return strtr($text,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'bcdefghijklmnopqrstuvwxyzaBCDEFGHIJKLMNOPQRSTUVWXYZA'
);
}
function rot25($text)
{
return strtr($text,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'zabcdefghijklmnopqrstuvwxyZABCDEFGHIJKLMNOPQRSTUVWXY'
);
}
}
print Text::rot25("Wjfm Tqbß opdi nju Lmbttfo voe Pckflufo!");
?>