Vererbung
Vererbung ist ein gängiges Prinzip in der Programmierung und PHP unterstützt dieses ebenfalls. Mittels Vererbung könnt ihr verschiedene Hierarchiestufen in euren Klassen ermöglichen. Funktionen die in einer Oberklasse programmiert wurden, vererben sich auf die Unterklassen und können dort verwendet werden.
Inhaltsverzeichnis
Vererbung in 30 Sekunden
Um von einer Klasse zu erben, existiert in PHP das Schlüsselwort extends :
<?php class Oberklasse { function HelloWorld() { echo "Hello World <br>"; } } class Unterklasse extends Oberklasse { function HelloWorld() { parent::HelloWorld(); echo "Hallo Welt <br>"; } } class Unterunterklasse extends Unterklasse { function HelloWorld() { echo "Bonjour monde <br>"; } } $object = new Unterklasse(); $object->HelloWorld(); ?>
Im obigen Beispiel haben wir eine Oberklasse, dann eine Unterklasse die von der Oberklasse erbt und eine Unterunterklasse, die mittels extends von der Unterklasse erbt.
In alle drei Klassen ist die Methode HelloWorld() definiert, die sich jeweils überschreiben. Möchte man die selbe Methode in der Eltern-Klasse aufrufen, so geht dies mittels parent::HelloWorld() .
Vererbung ausführlich erläutert
Wem die vorherige Anleitung zu knapp war, der freut sich hoffentlich über das nachfolgende, längere Beispiel. Angenommen wir haben einen Online-Shop indem wir verschiedene Produktegruppen anbieten, beispielsweise Bücher, Filme, und CDs. Jede dieser Produktgruppen ist als eigene Klasse definiert:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php class Buch { public $titel; public $preis; public $autor; function gesamtpreis($stueckzahl) { return $preis*$stueckzahl; } } class Film { public $titel; public $preis; public $regisseur; function gesamtpreis($stueckzahl) { return $preis*$stueckzahl; } } class CD { public $titel; public $preis; public $band; function gesamtpreis($stueckzahl) { return $preis*$stueckzahl; } } ?> |
Wie ihr erahnen könnt, führt solch eine Klassenkonstruktion zu sehr viel redundantem Programmcode. Für Bücher, Filme als auch Musik haben wir die Eigenschaften Titel und Preis sowie eine Methode gesamtpreis($stueckzahl), dennoch müssen wir diese für alle drei Produktgruppen neu implementieren. Schlimmer noch, ändert sich unser Preissystem, z.B. möchten wir ab 10 bestellten Artikeln einen Rabatt von 20% einräumen, müssten wir in allen drei Klassen den Code für die Methode gesamtpreis() anpassen.
Um diesen Code zu vereinfachen, empfiehlt es sich eine Elternklasse zu definieren, die alle ähnlichen Funktionalitäten vereint:
<?php class Produkt { public $titel; public $preis; function gesamtpreis($stueckzahl) { $gesamtpreis = $this->preis*$stueckzahl; if($stueckzahl >= 10) { $gesamtpreis = $gesamtpreis*0.8; } return $gesamtpreis; } } class Buch extends Produkt { public $autor; } class Film extends Produkt { public $regisseur; } class CD extends Produkt { public $band; } $buch = new Buch(); $buch->titel = "PHP lernen leicht gemacht"; $buch->preis = 20.00; $menge = 5; echo "$menge Bücher von <i>$buch->titel</i> je $buch->preis Euro kosten: ".$buch->gesamtpreis($menge)."<br>"; $film = new Film(); $film->titel = "Zurück in die Zukunft"; $film->preis = 10.00; $menge = 15; echo "$menge Filme von <i>$film->titel</i> je $film->preis Euro kosten: ".$film->gesamtpreis($menge)." Euro<br>"; ?>
Hier haben wir zuerst die Hauptklasse Produkt definiert. Jeder unserer Artikel hat einen Titel sowie einen Preis. Ebenfalls haben wir in der Hauptklasse die Methode gesamtpreis() definiert, die den Gesamtpreis berechnet falls der Kunde eine gewisse Anzahl bestellt. Bestellt dieser 10 oder mehr Artikel, erhält dieser 20% Rabatt auf den Artikel.
Weiter haben wir nun die drei Klassen Buch, Film und CD definiert, die mittels extends von der Klasse Produkt alle public und protected Eigenschaften erben (siehe Sichtbarkeit in OOP). Somit es es nicht mehr nötig in diesen Klassen erneut die selben Eigenschaften oder Methoden zu programmieren, sondern diese werden von der Elternklasse (hier Produkt) übernommen. Ändert ihr den Code der Methode gesamtpreis(), so müsst ihr dies im obigen Fall nur an einer einzigen Stelle machen und nicht mehr wie zuvor an drei verschiedenen Positionen in eurem Code.
Zugriff und Überschreibung von Eigenschaften und Methoden
Nun wollen wir das obige Beispiel erweitern und der Rabatt soll abhängig von der Produktgruppe sein. Für Bücher wollen wir ab einer Stückzahl von 10 einen Rabatt von 20% einräumen, für Filme einen Rabatt von 5% ab zwei Filmen und für CDs gibt es keinen Rabatt. Der aktualisierte Code könnte so aussehen:
<?php class Produkt { public $titel; public $preis; function gesamtpreis($stueckzahl) { $gesamtpreis = $this->preis*$stueckzahl*$this->mengenrabatt($stueckzahl); return $gesamtpreis; } function mengenrabatt($stueckzahl) { return 1; } } class Buch extends Produkt { public $autor; function mengenrabatt($stueckzahl) { if($stueckzahl >= 10) { return 0.8; //20% Rabatt } else { return 1; } } } class Film extends Produkt { public $regisseur; function mengenrabatt($stueckzahl) { if($stueckzahl >= 2) { return 0.95; //5% Rabatt } else { return 1; } } } class CD extends Produkt { public $band; } $buch = new Buch(); $buch->titel = "PHP lernen leicht gemacht"; $buch->preis = 20.00; $menge = 5; echo "$menge Bücher von <i>$buch->titel</i> je $buch->preis Euro kosten: ".$buch->gesamtpreis($menge)."<br>"; $film = new Film(); $film->titel = "Zurück in die Zukunft"; $film->preis = 10.00; $menge = 15; echo "$menge Filme von <i>$film->titel</i> je $film->preis Euro kosten: ".$film->gesamtpreis($menge)." Euro<br>"; ?>
In userer Hauptklasse Produkt fügen wir die Methode mengenrabatt() ein. Standardmäßig existiert kein Rabatt, deswegen gibt die Funktion den Wert 1 zurück.
Diese Methode überschreiben wir in der Klasse Buch. Statt dem Standardverhalten aus der Elternklasse, möchten wir dort ein neues Verhalten festlegen. Falls mehr als 10 Bücher bestellt sind, wird ein Rabatt von 20% gewährt (d.h. der Preis wird mit 0.8 multipliziert). In der Film-Klasse machen wir das selbe, nur dass es dort 5% Rabatt ab 2 Filmen gibt.
Ruft ihr nun für das Objekt die Methode gesamtpreis() auf, so nutzt diese stets die "neueste" Definition von mengenrabatt(), also die Definition, die am tiefsten in der Hierarchie ist. In diesem Fall wird für Objekte der Klassen Buch und Film die entsprechende Methode in diesen Klassen genutzt. Für Objekte der Klasse CD wird dagegen bei Aufruf der Methode mengenrabatt() die Methode aus Produkt verwendet, da wir in CD selbst die Methode nicht neu definiert haben / nicht überschrieben haben.
Vererbung über mehrere Generationen
Wie eingangs schon angedeutet, lassen sich Klassen auch über mehrere Generationen vererben. Beispielsweise wollen wir unterscheiden zwischen realen, physikalischen Gegenständen, wie beispielsweise einem Buch, und digitalen Gegenständen wie ein eBook oder ein Video-Stream. Eine mögliche Klassenstruktur könnte so aussehen:
<?php class Produkt { public $titel; public $preis; function gesamtpreis($stueckzahl) { $gesamtpreis = $this->preis*$stueckzahl*$this->mengenrabatt($stueckzahl); return $gesamtpreis; } function mengenrabatt($stueckzahl) { return 1; } } class RealesProdukt extends Produkt { public $lagerbestand; function istVorraetig() { //Überprüfen ob Produkt im Lager vorhanden ist return $this->lagerbestand> 0; } } class Buch extends RealesProdukt { public $autor; } //------------------------ class DigitalesProdukt extends Produkt { public $downloadlink; } class eBook extends DigitalesProdukt { public $dateiformat; } $buch = new Buch(); $buch->lagerbestand = "17"; if($buch->istVorraetig()) { echo "Das Buch ist vorrätig<br>"; } else { echo "Kein Buch mehr im Lager<br>"; } $eBook = new eBook(); $eBook->downloadlink = "htttp://website.de"; echo "Der Downloadlink ist: ".$eBook->downloadlink;
Bei Objekten der Klasse Buch können wir auf alle (public und protected) Methoden und Eigenschaften aus den Klassen Buch, RealesProdukt und Produkt zugreifen. Bei der Klasse eBook dagegen können wir auf die Methoden und Eigenschaften der Klassen eBook, DigitalesProdukt und Produkt zugreifen.
Das richtige Klassendesign
Wie man seine Klassen konstruiert, wie viele Haupt- und wie viele Kinderklassen man definiert ist nicht leicht zu entscheiden. Für den Anfang solltet ihr aber versuchen, eure Klassendesign nicht zu kompliziert zu gestalten, ansonsten verliert man schnell den Überblick.
Neben entsprechender Übung empfiehlt es sich auch, ein sogenanntes Klassendiagramm zu zeichnen. In diesem Diagramm zeichnet ihr alle Klassen und deren Beziehungen zueinander ein. Dies hilft um den Überblick zu behalten.
Autor: Nils Reimers