PHP, ursprünglich eine prozedurale Skriptsprache, hat erst mit Version 4 brauchbare objektorientierte Züge angenommen. In diesem Kapitel will ich versuchen zu zeigen, wie man objektorientiert in PHP programmiert und auch ein paar Beispiele geben, um zu sehen, wie man von der Problemstellung zur OO-Umsetzung kommt und auch, daß sich das bißchen Mehraufwand wirklich lohnt (Stichwort Übersichtlichkeit/Wiederverwendbarkeit). Das folgende Kapitel gilt der Einfachheit halber nur für PHP 4.06 aufwärts.
Zu beachten ist, daß der Name stdClass in PHP4 reserviert ist, ebenso wie alle Methodennamen, die mit zwei Underscores (z.B. __sleep, __wakeup) beginnen.
Beispiel:
class DSP_CR { function Auto() { ... } ... }
Eine Besonderheit beim Umgang mit Konstruktoren ist schließlich noch der implizite Aufruf des Konstruktors der Basisklasse in dem Fall, daß eine Klasse keinen Konstruktor besitzt. Die Unfähigkeit von PHP3, dies zu meistern, ist auch der Grund, warum nach Möglichkeit PHP4 eingesetzt werden sollte.
Beispiel:
class Auto { ... } class DSP_CR extends Auto { ... }
Beispiel:
class Auto { var $baujahr; var $farbe; ... } class DSP_CR extends Auto { // hier in Ermangelung von Methoden noch // nicht über den Konstruktor gesetzt var $farbe = "metallic"; ... }
Zur Referenzierung von Attributen mehr weiter unten (19.2.1).
Beispiel:
class Auto { var $baujahr; var $farbe; function Auto() { ... } ... } class DSP_CR extends Auto { // Da DSP_CR von Auto erbt, müssen hier keine Variablen // definiert werden. Die geerbten Attribute (und auch // Methoden) der Basisklasse kann man natürlich nutzen. function DSP_CR($farbe) { if (empty($farbe)) $this->farbe = "metallic"; else $this->farbe = $farbe; } function setzeFarbe($farbe) { ... } ... }
Zur Referenzierung von Methoden mehr weiter unten (19.2.1).
Folgende Syntax, in der das Schlüsselwort new neu eingeführt wird, erzeugt in PHP ein Objekt:
$meinWagen = new DSP_CR();
Das Objekt meinWagen wird hierbei von der Klasse DSP_CR
erzeugt, die dazu ihren parameterlosen Konstruktor benutzt. Der Quelltext der Klasse
muß natürlich bereits vom PHP-Parser gelesen worden sein, bevor eine solche
Zuweisung erfolgen kann. Üblicherweise lagert man Klassendefinitionen in
Include-Dateien aus, z.B. nach dem Schema <KlassenName>.inc, und
importiert sie mit include() oder require().
Wird anstelle des parameterlosen Konstruktor einer verwendet, der Parameter erwartet, erfolgt die Objektinitialisierung ganz analog, indem man wie bei Funktionsaufrufen die Parameter in entsprechender Reihenfolge innerhalb der runden Klammern angibt.
Auf das so erzeugte Objekt kann nun so lange zugegriffen werden, bis es explizit auf NULL gesetzt wird oder das Skript beendet wird. Allgemein kann man mit Objektvariablen übrigens ganz analog zu normalen Primitivtyp-Variablen Objekt-Zuweisungen durchführen. Folgender Code ist somit gültig:
$meinWagen = new DSP_CR(); $meinZweitwagen = new DSP_CR(); $meinWagen = $meinZweitwagen;
Achtung: Bis vor der Zuweisung waren meinWagen und meinZweitwagen noch völlig unterschiedliche Objekte! Danach natürlich nicht mehr!
Innerhalb einer Klasse gehören die Attribute und Methoden der Klasse immer dem gerade zu betrachtenden Objekt, das wie gesagt nur außerhalb der Klasse existiert. Da die Klasse quasi den Plan für ein Objekt darstellt, liegt der Gedanke nahe, daß dieser Plan in dem Moment, wo man ein spezielles Objekt betrachtet, die Eigenschaften des Objekts annimmt. Wenn also die Klasse eigentlich nur der Bauplan für Objekte ist, so verwandelt sie sich doch in dem Moment, wo man ein einzelnes Objekt betrachtet, in das Objekt selbst! Dadurch hat man die Möglichkeit, ein Objekt abhängig von seinen ganz persönlichen Eigenschaften zu verändern, ohne es beim Erstellen der Klasse schon zu kennen! :-)
Will man also Attribute oder Methoden referenzieren, beginnt man mit dem für
PHP typischen Dollarzeichen, gefolgt vom Namen des zu referenzierenden
Objektes, einem Strichpfeil (->) und dem Namen des Attributs bzw. der
Methode. Will man innerhalb einer Klasse auf die Attribute oder Methoden
derselben Klasse zugreifen (die wie gesagt in dem Moment, wo man ein Objekt
betrachtet, das Objekt selbst darstellt), muß man das Schlüsselwort
this anstelle des Objektnamens verwenden.
Ein primitves Beispiel:
class Auto { ... function Erfinder() { return "Etienne Lenoir, Carl Benz"; } ... } echo Auto::Erfinder();
Beispiel: Angenommen, wir hätten in der Klasse DSP_CR die Methode lenke(drehung) der Klasse Auto überschrieben, von der DSP_CR erbt. Will man nun innerhalb von DSP_CR auf die gleichnamige Methode der Basisklasse Auto zugreifen, kommt folgende Syntax zum Einsatz:
... function lenke($drehung) { // Servo aktivieren $drehung = $this->servo($drehung); // Methode der Basisklasse aufrufen parent::lenke($drehung); } ...
/** * Auto Klasse * * @author DSP * @version 1.0 */ class Auto { /** * Baujahr * @type int */ var $baujahr; /** * Farbe * @type string */ var $farbe; /** * Konstruktor * @param $farbe gewünschte Farbe */ function Auto($farbe) { $this->farbe = $farbe; $this->baujahr = date(); } /** * Ändert die Wagenfarbe * @param $farbe gewünschte Farbe */ function setzeFarbe($farbe) { $this->farbe = $farbe; } /** * Welches Baujahr? * * @return Das Baujahr * @returns string */ function Baujahr() { return $this->baujahr; } } /** * DSP CR Klasse * Konstruktor der Basisklasse wird implizit aufgerufen * * @author DSP * @version 1.0 */ class DSP_CR extends Auto { /** * Konstruktor * eigentlich wäre gar keiner notwendig, * aber wir wollen ja die Farbe initialisieren... */ function DSP_CR($farbe) { if (empty($farbe)) $farbe = "metallic"; parent::Auto($farbe); } }
Alternativ zum letzten Codeteil, in dem der Konstruktor der Basisklasse aufgerufen wird, kann man auch folgende Syntax verwenden, die unabhängig vom Namen der Basisklasse ist - in Java würde man das übrigens mit einem einfachen super(parameter) machen.
class A extends B { function A($param) { $parent = get_parent_class($this); $this->$parent($param); } }
Zuerst jedoch die Methode, die Christoph bevorzugt:
Man abstrahiert von der Problemstellung soweit, daß man Subjekte, also Teile
des Ganzen mit individuellen Eigenschaften, identifizieren kann. In der
Programmierpraxis sind das z.B. eigenständige Prozesse wie Datei-Ein-/Ausgabe
oder die jeweils zentrale Informationseinheit, deren Daten z.B. in einer
Datenbank festgehalten werden.
Hat man diese Abstraktion erst einmal geschafft, fällt es leicht, diese Subjekte
mit Objekten zu assoziieren, die zu einer zu erstellenden Klasse gehören. Die
Eigenschaften des Subjekts setzt man der OO-Logik folgend in Form von Attributen
(Klassenvariablen) um. Schließlich muß man sich noch über die Schnittstelle
Gedanken machen -- i.A. sind Konstruktor, Lese- und Schreibzugriff für die
Eigenschaften und einige zusätzliche Funktionen nötig, die allesamt als Methoden
der Klasse implementiert werden.
Und nun mein Ansatz:
Als erstes sollte man das Problem auf die zentrale Funktionalität reduzieren,
denn diese bildet i.A. die Schnittstelle der späteren Klasse, also die
benötigten Funktionen. Darüber hinaus werden natürlich meist noch weitere
Funktionen benötigt, diese sind aber intern und könnten daher private deklariert werden.
Zu beachten ist auch, daß man in erster Linie die Funktionalität in eine Klasse steckt, die gemeinsame Daten (die späteren Attribute) verwendet. Hier nun einige Beispiele -- wer noch Anschauliches hat, kann diese gerne an Christoph schicken, der sich mit Objektorientierung in PHP mindestens so gut auskennt wie ich. ;-)
|
|
|
|
Schreibe eine Klasse Image, die mit Hilfe der
PHP-Image-Funktionenein leeres Bild erzeugt und die Methode show() implementiert. Leite von
dieser Klasse eine neue namens Pointab mit der Methode
set(x, y), die einen Punkt malt. Programmiere schließlich die Klassen
Circle (Kreis), Rectangle (Rechteck) als Erben von
Point sowie die Klasse Square (Quadrat), die
Rectangle erweitert. Erstelle von jeder malfähigen Klasse ein Objekt
und benutze es jeweils, um das jeweilige Symbol auszugeben.
Eine mögliche Lösung findet sich in Abschnitt B.7.