copyright (c)  2003   useGroup
PHP

Jochen Stärk
PHP-Tutorial
Klassen und Objekte

nach untenKlassen
nach untenBeispiel
nach untenReferenzen auf den Owner
nach untenKonstruktoren
nach untenMethoden
nach untenVererbung
nach untenAufruf von Vaterfunktionen
nach untenÜbergabe von Objekten

Klassen

nach obenKlassen 

In Klassen kann man Variablen und Funktionen angeben, die dann eine logische Einheit bilden. Wenn man nur die Variablen betrachtet, ist eine Klasse ist also ein zusammengesetzter Datentyp, aus dem man Variablen (die dann Objekte genannt werden) ableiten kann.

Da die objekteigenen Funktionen (=Methoden) auf die Variablen des Objekts (=Eigenschaften) zugreifen können müssen, und gleichzeitig eine Trennung zu lokalen Variablen, die sich auf die Funktion, in der sie definiert sind, zu verdeutlichen, wird bei Zugriff auf die Eigenschaften ein Schlüsselwort vorangstellt, das immer "das aktuelle Objekt" bedeutet, $this. $this->variablenname (nicht $this->$variablenname) greift also auf eine Variable zu, die im gesamten Objekt gültig ist, während $variablenname eine lokale Variable ist, die nur in der aktuellen Funktion gültig ist.

Um zusätzlich, unabhängig davon, welche Funktionen abgelaufen sind, die auf Klassenvariablen hätten zugreifen können, ein Zugriff von außerhalb des Objektes auf die Klassenvariablen zu erlauben, werden diese mit dem Schlüsselwort var in der Klasse deklariert.


Beispiel

nach obenBeispiel 

Um ein Objekt aus einer Klasse zu erzeugen, verwendet man new.


class Golf
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function beschleunige()
{
  $this->geschwindigkeit=$this->geschwindigkeit+10;
}
function bremse()
{
  $this->geschwindigkeit=$this->geschwindigkeit-10;
}
}

Golf $meinGolf=&new Golf();  // Golf ist der Klassenname, $meinGolf ist der Instanzname,
			    // also das Objekt.
$meinGolf->beschleunige();
$meinGolf->beschleunige();
$meinGolf->bremse();
echo $meinGolf->geschwindigkeit;


Referenzen auf den Owner

nach obenReferenzen auf den Owner 

Tatsächlich gibt es manchmal Probleme, wenn man, wie in der Literatur beschrieben, Golf $meinGolf=new Golf(); verwendet. Es ist nämlich gar nicht mal so unüblich, dass das instanziierte Objekt dem Objekt, das es aufgerufen hat, noch etwas zu sagen hat. Üblicherweise baut man sich dann einen Konstruktor, in dem man eine Referenz auf das aufrufende Objekt übergibt; der Konstruktor speichert sich dann die Referenz, üblicherweise in einer Klassenvariable wie parent oder owner und die Member-Methoden können dann per $this->owner->funktionsname() eine Funktion des Vaterobjekts aufrufen.

Ich persönlich bevorzuge den Variablennamen owner, weil parent in PHP eigentlich die Vaterklasse, also die Klasse von der geerbt wurde, bezeichnet, und nicht die Klasse, aus der aufgerufen wurde.

Das Problem mit dem = new ist, dass es new aufruft und wieder eine Kopie übergibt, während $instanzname = & new Objektname(); die Instanz übergibt, die auch kreiert wird.

<?php

class myC1
{
  var
    $greeting;
  var  $theC2;
  function sayHello()
  {
      echo $this->greeting;
  }
  function myC1()
  {
      $this->greeting="Howdy";
      $this->theC2=new myC2($this);
  }



}

class myC2
{
  var $owner;
  function myC2(&$owner)
  {
    $this->owner=&$owner;
    $this->owner->greeting="bah";
  }
  function something()
  {
    $this->owner->greeting="hulla";
    // This is supposed to change myC1's greeting to hulla, overwriting "bah".
  }
 }


$test=new myC1(); //$test=&new myC1();
$test->theC2->something();
$test->sayHello();

?>
Das Beispiel gibt unter PHP4 "bah" aus - anstelle "hulla", wie es offensichtlich geplant und beabsichtigt war. $test=&new myC1(); dagegen gibt hulla aus. Generell schadet =& new nie, während man mit = new manchmal Probleme geben kann. Unter PHP5 ist das =& auch in dem Beispiel unnötig, da Objekte standardmäßig als Referenz übergeben werden. Also benutzen Sie es nicht! Sie würden Code riskieren, den man auch unter PHP4 ausführen kann!


Konstruktoren

nach obenKonstruktoren 

Wie Sie im Beispiel sehen, ist "geschwindigkeit" vor Gebrauch gar nicht initialisiert. Außerdem wird new Golf() aufgerufen, obwohl Golf eigentlich keine Funktion ist, sondern ein Datentyp (eine Klasse). Allerdings wird jeder Klasse eine Funktion zugeordnet, die so heißt wie die Klasse, und die Konstruktion neuer Objekte erledigt. Und diesen Konstruktor kann man verwenden, um Variablen zu initialisieren. Er wird innerhalb der Klasse bei der Erzeugung eines neuen Objektes zuerst aufgerufen. Angenommen, "mein Golf", oder besser die ganze Klasse Golf, rollt bei der Kontruktion langsam vom Band:

class Golf
{
var
  $geschwindigkeit;
  function Golf()
  {
    $this->geschwindigkeit=2;
  }
//...
}
Sie können dem Konstruktor auch Werte übergeben, etwa die Farbe:
class Golf
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
//...
}
Golf $meinGolf=&new Golf("Blau");


Methoden

nach obenMethoden 

Wie im beispiel beschrieben kann ich Funktionen in Klassen relativ einfach deklarieren. Ich kann Sie durch objektname->Funktionsname auch relativ einfach nutzen. Das bedeutet auch, dass im Klasseninternen Gebrauch ein $this-> voranzustellen ist.

class Golf
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function beschleunige($wert)
{
  $this->geschwindigkeit=$this->geschwindigkeit+$wert;
}
function bremse($wert)
{
  $this->beschleunige((-1)*$wert);
}
}


Vererbung

nach obenVererbung 

Eine der Gründe, warum Objektorientierung so erfolgreich ist, liegt darin, dass eine Einheit aus Funktionen und Variablen in der Lage ist, Zustände abzubilden. So war "meinGolf", nachdem er zweimal Beschleunigt hatt, 20 Einheiten schnell, bevor er wieder auf 10 Einheiten abgebremst wurde. Diese Zustände zu verändern, ist wichtig, um das Objekt anpassen zu können, und es über mehrere Operationen hinweg tun zu können, hilft dem Programmierer. So kann ich alle Golf mit Standarteigenschaften und Standartwerten konstruieren, danach vielleicht ein paar "Blumen" giessen(), dann ein paar Golf klauen() und umspritzen().

Sie können also Methoden verwenden, um Eigenschaften auszulesen oder zu ändern, und das führt zum Grundsatz, dass von außen nur die Schnittstellen zum Objekt (einige Methoden) sichtbar sein sollen, während die Eigenschaften und die genaue Arbeitsweise der Methoden "geheim gehalten" werden sollte, um es zu ermöglichen, diese später noch zu ändern. Das nennt man Black-Box-Prinzip, bei dem zwar die Box, aber nicht deren Inhalt sichtbar ist. Denn wenn es möglich ist, Klassen noch zu verbessern (das tut man oft durch geänderte Methoden und geänderte Datentypen), macht das Erweitern von Klassen erst richtig Spaß.

Klassen können nämlich um Methoden und Eigenschaften erweitert werden, ohne sie zu verändern. Es werden einfach neue Klassen geschaffen, die alle Methoden und Eigenschaften von den "Vaterklassen" erben. Sie erben damit nicht nur alle Namen von Methoden, sondern auch die Methoden selbst.

class Fahrzeug
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function Fahrzeug()
{
}
function beschleunige()
{
  $this->geschwindigkeit=$this->geschwindigkeit+10;
}
function bremse()
{
  $this->beschleunige(-10);
}
}

class Golf extends Fahrzeug
{
function bremseABS()
{
  $this->beschleunige(-12);  // Die Funktion kommt aus der Vaterklasse,
  			     // wird hier aber wie eine eigene Funktion
			     // angesprochen

}

}

Golf $meinGolf=&new Golf();  // Golf ist der Klassenname, $meinGolf ist der Instanzname,
			    // also das Objekt.
$meinGolf->beschleunige();
$meinGolf->beschleunige();
$meinGolf->bremseABS();
echo $meinGolf->geschwindigkeit;


Sie müssen in PHP 4, wenn Sie den Konstruktor einer verebten Klasse ändern, bei Bedarf den Konstruktor der Vaterklasse aufrufen:
class Golf extends Fahrzeug
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    parent::Fahrzeug();
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
}
Golf $meinGolf=&new Golf("Blau");


Aufruf von Vaterfunktionen

nach obenAufruf von Vaterfunktionen 

Entgegen einem richtigen Erbfall, der mit der Anzahl Erben komplizierter wird, wird das objektorientierte Vereben umso schöner und leichter, je öfter man eine Vaterklasse nutzen kann: Zusätzlich zum Golf erbt z.B. auch ein LKW vom Fahrzeug:

class LKW extends Fahrzeug
{
  var
    $maxBeladung;

  function LKW()
  {
    $parent::Fahrzeug();
    $this->maxBeladung=30;
  }
  function belade($gewicht)
  {
    $this->maxBeladung=$this->maxBeladung-$gewicht;
  }

}
In dem Fall muss die Behandlung von Bremsen und Beschleunigen nicht mehr programmiert werden, weil das Verhalten und die Eigenschaften vererbt sind. Dummerweise wird ein erweiterter Konstruktor benötigt, und PHP sort nicht, wie andere Sprachen, für den automatischen aufruf des Vaterkonstruktors. In der Zeile $parent::Fahrzeug(); holen wir das nach.


Übergabe von Objekten

nach obenÜbergabe von Objekten 

Es spricht nichts dagegen, eine Funktion wie schrottpresse($meinGolf) mit einem Golf-Objekt aufzurufen. Abgesehen davon, dass man das verschrotten vielleicht in die Fahrzeug-Klasse implementieren könnte, müssten Sie beim obigen Beispiel nur aufpassen, das Sie das richtige Auto verschrotten. Denn jede Funktion wird per default "by value", also in dem Fall mit einer Kopie des meinGolf-Objekts aufgerufen. Sie sollten daher sichergehen, dass die entsprechende Funktion eine Referenz des Objekts bekommt und keine Kopie.

Beispiel:

class Golf
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
}

class Lackiererei {
  function spritze_um($GolfObjekt, $neueFarbe)
  {
     $GolfObjekt->farbe=$neueFarbe;
  }
}

$meineLackiererei =& new Lackiererei();
$meinGolf =& new Golf("Blau");
// Bitte jetzt nicht fragen, warum ich einen NEUEN Golf umspritze ;-)
$meineLackiererei->spritze_um($meinGolf, "Rot");
echo $meinGolf->farbe;
Das liefert ihnen weiterhin einen Blauen Golf, weil Sie nur eine Kopie des blauen Golfs, die auch nur ganz kurz in der Lackiererei "gelebt" hat, umgespritzt haben. Richtig wäre gewesen:
  function spritze_um(&$GolfObjekt, $neueFarbe)
  {
     $GolfObjekt->farbe=$neueFarbe;
  }
Siehe Referenzen.


copyright (c)  2003   useGroup