Polymorphismus in PHP verstehen und anwenden

In der objektorientierten Programmierung ist Polymorphismus ein mächtiges und grundlegendes Werkzeug. Es kann verwendet werden, um einen organischen Fluss in Ihrer Anwendung zu erzeugen. In diesem Tutorial wird das allgemeine Konzept des Polymorphismus beschrieben und wie dieser in PHP problemlos implementiert werden kann.


Was ist Polymorphismus??

Polymorphismus ist ein langes Wort für ein sehr einfaches Konzept.

Polymorphismus beschreibt ein Muster in der objektorientierten Programmierung, in dem Klassen unterschiedliche Funktionen haben und sich eine gemeinsame Schnittstelle teilen.

Das Schöne an Polymorphismus ist, dass der Code, der mit den verschiedenen Klassen arbeitet, nicht wissen muss, welche Klasse er verwendet, da sie alle auf dieselbe Weise verwendet werden.

Eine echte Weltanalogie für Polymorphismus ist ein Knopf. Jeder weiß, wie man eine Taste benutzt: man drückt einfach darauf. Was eine Schaltfläche "tut", hängt jedoch davon ab, mit was sie verbunden ist und in welchem ​​Kontext sie verwendet wird - das Ergebnis hat jedoch keinen Einfluss auf die Verwendung der Schaltfläche. Wenn Ihr Chef Sie auffordert, eine Taste zu drücken, haben Sie bereits alle Informationen, um die Aufgabe auszuführen.

In der Programmierwelt wird Polymorphismus verwendet, um Anwendungen modularer und erweiterbarer zu machen. Anstelle von chaotischen Bedingungsanweisungen, die unterschiedliche Vorgehensweisen beschreiben, erstellen Sie austauschbare Objekte, die Sie entsprechend Ihren Anforderungen auswählen. Das ist das grundlegende Ziel des Polymorphismus.


Schnittstellen

Ein integraler Bestandteil des Polymorphismus ist die gemeinsame Schnittstelle. Es gibt zwei Möglichkeiten, eine Schnittstelle in PHP zu definieren: Schnittstellen und abstrakte Klassen. Beide haben ihre Verwendung, und Sie können sie mischen und anpassen, wenn Sie in Ihre Klassenhierarchie passen.

Schnittstelle

Eine Schnittstelle ähnelt einer Klasse, außer dass sie keinen Code enthalten kann. Eine Schnittstelle kann Methodennamen und -argumente definieren, nicht jedoch den Inhalt der Methoden. Alle Klassen, die eine Schnittstelle implementieren Muss Implementieren Sie alle von der Schnittstelle definierten Methoden. Eine Klasse kann mehrere Schnittstellen implementieren.

Eine Schnittstelle wird mit '' deklariert.Schnittstelle' Stichwort:

Schnittstelle MyInterface // Methoden

und wird an eine Klasse angehängtimplementiert'Schlüsselwort (mehrere Schnittstellen können durch Auflisten mit Kommas getrennt implementiert werden):

Klasse MyClass implementiert MyInterface // Methoden

Methoden können wie in einer Klasse im Interface definiert werden, außer ohne den Body (der Teil zwischen den geschweiften Klammern):

Schnittstelle MyInterface public function doThis (); öffentliche Funktion doThat (); öffentliche Funktion setName ($ name); 

Alle hier definierten Methoden müssen genau wie beschrieben in implementierenden Klassen enthalten sein. (Lesen Sie die Code-Kommentare unten)

// VALID-Klasse MyClass implementiert MyInterface protected $ name; öffentliche Funktion doThis () // Code, der dies ausführt öffentliche Funktion doThat () // Code, der das ausführt öffentliche Funktion setName ($ name) $ this-> name = $ name;  // INVALID-Klasse MyClass implementiert MyInterface // fehlt doThis ()! private function doThat () // dies sollte öffentlich sein!  public function setName () // das Namensargument fehlt! 

Abstrakte Klasse

Eine abstrakte Klasse ist eine Mischung aus einer Schnittstelle und einer Klasse. Es kann sowohl Funktionalität als auch Schnittstelle definieren (in Form abstrakter Methoden). Klassen, die eine abstrakte Klasse erweitern Muss Implementieren Sie alle abstrakten Methoden, die in der abstrakten Klasse definiert sind.

Eine abstrakte Klasse wird wie Klassen mit dem Zusatz '' deklariert.abstrakt' Stichwort:

abstrakte Klasse MyAbstract // Methoden

und wird an eine Klasse angehängterweitert' Stichwort:

Klasse MyClass erweitert MyAbstract // Klassenmethoden

Reguläre Methoden können in einer abstrakten Klasse genau wie in einer regulären Klasse definiert werden, ebenso wie abstrakte Methoden (mit dem 'abstrakt' Stichwort). Abstrakte Methoden verhalten sich wie Methoden, die in einer Schnittstelle definiert sind, und müssen genau so implementiert werden, wie sie durch das Erweitern von Klassen definiert werden.

abstrakte Klasse MyAbstract public $ name; public function doThis () // do this abstrakte öffentliche Funktion doThat (); abstrakte öffentliche Funktion setName ($ name); 

Schritt 1: Identifizieren Sie das Problem

Stellen wir uns vor, Sie haben eine Artikel Klasse, die für die Verwaltung von Artikeln auf Ihrer Website verantwortlich ist. Es enthält Informationen zu einem Artikel, einschließlich Titel, Autor, Datum und Kategorie. So sieht es aus:

Klasse poly_base_Article public $ title; öffentlicher $ Autor; öffentliches $ Datum; öffentliche $ -Kategorie; öffentliche Funktion __construct ($ title, $ author, $ date, $ category = 0) $ this-> title = $ title; $ this-> author = $ author; $ this-> date = $ date; $ this-> category = $ category; 

Hinweis: Die Beispielklassen in diesem Lernprogramm verwenden die Namenskonvention von "package_component_Class". Dies ist eine übliche Methode, um Klassen in virtuelle Namespaces zu unterteilen, um Namenskollisionen zu vermeiden.

Jetzt möchten Sie eine Methode hinzufügen, um die Informationen in verschiedenen Formaten wie XML und JSON auszugeben. Sie könnten versucht sein, so etwas zu tun:

class poly_base_Article //… öffentliche Funktion write ($ type) $ ret = "; switch ($ type) case 'XML': $ ret = '
'; $ ret. = ''. $ obj-> title. ''; $ ret. = ''. $ obj-> Autor. ''; $ ret. = ''. $ obj-> Datum. ''; $ ret. = ''. $ obj-> Kategorie. ''; $ ret. = '
'; brechen; Fall 'JSON': $ array = array ('article' => $ obj); $ ret = json_encode ($ array); brechen; return $ ret;

Dies ist eine Art hässliche Lösung, aber es funktioniert - vorerst. Fragen Sie sich, was in der Zukunft passiert, wenn wir weitere Formate hinzufügen möchten? Sie können die Klasse weiter bearbeiten, mehr und mehr Fälle hinzufügen, aber jetzt verwässern Sie nur Ihre Klasse.

Ein wichtiges Prinzip von OOP ist, dass eine Klasse eine Sache tun sollte und dass es gut sein sollte.

Vor diesem Hintergrund sollten Bedingungsanweisungen eine rote Flagge sein, um anzuzeigen, dass Ihre Klasse zu viele verschiedene Dinge versucht. Hier kommt der Polymorphismus ins Spiel.

In unserem Beispiel werden zwei Aufgaben dargestellt: Artikel verwalten und Daten formatieren. In diesem Tutorial werden wir unseren Formatierungscode in eine neue Gruppe von Klassen umwandeln und herausfinden, wie einfach es ist, Polymorphismus zu verwenden.


Schritt 2: Definieren Sie Ihre Schnittstelle

Als erstes sollten wir die Schnittstelle definieren. Es ist wichtig, über Ihre Schnittstelle nachzudenken, da Änderungen an der Oberfläche unter Umständen Änderungen am aufrufenden Code erfordern. In unserem Beispiel verwenden wir eine einfache Schnittstelle, um unsere einzige Methode zu definieren:

Schnittstelle poly_writer_Writer public function write (poly_base_Article $ obj); 

So einfach ist das. Wir haben ein Publikum definiert schreiben() Methode, die ein Artikelobjekt als Argument akzeptiert. Alle Klassen, die die Writer-Schnittstelle implementieren, verfügen über diese Methode.

Spitze: Wenn Sie den Typ von Argumenten einschränken möchten, die an Ihre Funktionen und Methoden übergeben werden können, können Sie, wie im Abschnitt, Typhinweise verwenden schreiben() Methode; Es akzeptiert nur Objekte des Typs poly_base_Artikel. Leider werden Rückgabewerthinweise in aktuellen PHP-Versionen nicht unterstützt. Daher müssen Sie sich um die Rückgabewerte kümmern.


Schritt 3: Erstellen Sie Ihre Implementierung

Wenn Ihre Schnittstelle definiert ist, ist es an der Zeit, die Klassen zu erstellen, die tatsächlich etwas tun. In unserem Beispiel haben wir zwei Formate, die wir ausgeben möchten. Wir haben also zwei Writer-Klassen: XMLWriter und JSONWriter. Es ist an diesen, die Daten aus dem übergebenen Artikel-Objekt zu extrahieren und die Informationen zu formatieren.

So sieht XMLWriter aus:

Klasse poly_writer_XMLWriter implementiert poly_writer_Writer public function write (poly_base_Article $ obj) $ ret = '
'; $ ret. = ''. $ obj-> title. ''; $ ret. = ''. $ obj-> Autor. ''; $ ret. = ''. $ obj-> Datum. ''; $ ret. = ''. $ obj-> Kategorie. ''; $ ret. = '
'; return $ ret;

Wie Sie der Klassendeklaration entnehmen können, verwenden wir die implementiert Stichwort zur Implementierung unserer Schnittstelle. Das schreiben() Diese Methode enthält spezifische Funktionen zum Formatieren von XML.

Hier ist unsere JSONWriter-Klasse:

Klasse poly_writer_JSONWriter implementiert poly_writer_Writer public function write (poly_base_Article $ obj) $ array = array ('article' => $ obj); Rückgabe von json_encode ($ array); 

Der gesamte für jedes Format spezifische Code ist jetzt in einzelnen Klassen enthalten. Diese Klassen haben jeweils die alleinige Verantwortung für die Verarbeitung eines bestimmten Formats und nichts anderes. Kein anderer Teil Ihrer Anwendung muss sich aufgrund unserer Benutzeroberfläche darum kümmern, wie diese funktionieren, um sie verwenden zu können.


Schritt 4: Verwenden Sie Ihre Implementierung

Nachdem unsere neuen Klassen definiert wurden, ist es an der Zeit, unsere Artikel-Klasse erneut zu besuchen. Der gesamte Code, der im Original gelebt hat schreiben() Die Methode wurde in unsere neuen Klassen eingepreist. Jetzt müssen wir nur noch die neuen Klassen verwenden:

class poly_base_Article //… öffentliche Funktion write (poly_writer_Writer $ writer) return $ writer-> write ($ this); 

Diese Methode akzeptiert nun lediglich ein Objekt der Writer-Klasse (dh jede Klasse, die die Writer-Schnittstelle implementiert) schreiben() Methode, sich selbst zu übergeben ($ das) als Argument weiterleiten und dann ihren Rückgabewert direkt an den Client-Code weiterleiten. Sie müssen sich nicht mehr um die Details der Datenformatierung kümmern und können sich auf ihre Hauptaufgabe konzentrieren.

Einen Schriftsteller erhalten

Möglicherweise fragen Sie sich, wo Sie ein Writer-Objekt erhalten, da Sie ein Objekt an diese Methode übergeben müssen. Das liegt an Ihnen, und es gibt viele Strategien. Beispielsweise können Sie eine Factory-Klasse verwenden, um Anfragedaten zu übernehmen und ein Objekt zu erstellen:

class poly_base_Factory öffentliche statische Funktion getWriter () // Variable für Anforderungsaufforderung $ format = $ _REQUEST ['format']; // Konstruiere unseren Klassennamen und überprüfe seine Existenz $ class = 'poly_writer_'. $ format 'Schriftsteller'; if (class_exists ($ class)) // ein neues Writer-Objekt zurückgeben return new $ class ();  // Andernfalls schlagen wir fehl, neue Exception ('Nicht unterstütztes Format') zu werfen. 

Wie ich schon sagte, gibt es viele andere Strategien, abhängig von Ihren Anforderungen. In diesem Beispiel wählt eine Anforderungsvariable das zu verwendende Format. Er erstellt aus der Anforderungsvariablen einen Klassennamen, prüft, ob er existiert, und gibt ein neues Writer-Objekt zurück. Wenn unter diesem Namen keine vorhanden ist, wird eine Ausnahme ausgelöst, damit der Clientcode herausfinden kann, was zu tun ist.


Schritt 5: Setzen Sie alles zusammen

Wenn alles vorhanden ist, würde unser Client-Code alles so zusammenfügen:

$ article = new poly_base_Article ('Polymorphism', 'Steve', time (), 0); try $ writer = poly_base_Factory :: getWriter ();  catch (Ausnahme $ e) $ writer = new poly_writer_XMLWriter ();  echo $ article-> write ($ writer);

Zuerst haben wir ein Beispielartikelobjekt erstellt, mit dem gearbeitet werden kann. Dann versuchen wir, ein Writer-Objekt aus der Factory abzurufen und auf einen Standardwert (XMLWriter) zurückzugreifen, wenn eine Ausnahme ausgelöst wird. Schließlich übergeben wir den Artikel des Verfassers an unseren Artikel schreiben() Methode, das Ergebnis zu drucken.


Fazit

In diesem Tutorial habe ich Ihnen eine Einführung in Polymorphismus und eine Erläuterung der Schnittstellen in PHP gegeben. Ich hoffe, Ihnen ist klar, dass ich Ihnen nur einen möglichen Anwendungsfall für Polymorphismus gezeigt habe. Es gibt viele, viele weitere Anwendungen. Polymorphismus ist eine elegante Möglichkeit, hässlichen Bedingungsanweisungen in Ihrem OOP-Code zu entgehen. Es folgt dem Prinzip, dass Ihre Komponenten getrennt bleiben, und ist Bestandteil vieler Entwurfsmuster. Wenn Sie Fragen haben, zögern Sie nicht, in den Kommentaren nachzufragen!