Advertisement
  1. Code
  2. PHP

Vom prozeduralen zum objektorientierten PHP

Scroll to top
Read Time: 22 min

German (Deutsch) translation by Valentina (you can also view the original English article)

Dieses Tutorial wurde von einer Rede von Robert C. Martin inspiriert, die ich vor ungefähr einem Jahr gesehen habe. Das Hauptthema seines Vortrags ist die Möglichkeit, Die letzte Programmiersprache auszuwählen. Er spricht Themen wie warum sollte eine solche Sprache existieren? Und wie soll es aussehen? Wenn Sie jedoch zwischen den Zeilen lesen, gab es eine andere interessante Idee, die meine Aufmerksamkeit auf sich zog: die Einschränkungen, die jedes Programmierparadigma uns Programmierern auferlegt. Bevor wir uns also damit befassen, wie wir eine prozedurale PHP-App in eine objektorientierte umwandeln können, möchte ich vorab ein wenig Theorie behandeln.


Paradigme-Beschränkungen

Jedes Programmierparadigma schränkt unsere Fähigkeit ein, alles zu tun, was wir wollen. Jeder von ihnen nimmt etwas weg und bietet eine Alternative, um das gleiche Ergebnis zu erzielen. Modulare Programmierung nimmt unbegrenzte Programmgröße weg. Es erzwingt, dass der Programmierer Module mit maximaler Größe verwendet, und jedes Modul endet mit einer "go-to" -Anweisung an ein anderes Modul. Die erste Einschränkung betrifft also die Größe. Dann nimmt die strukturierte Programmierung und die prozedurale Programmierung die "go-to" -Anweisung weg und beschränkt den Programmierer auf Sequenz, Auswahl und Iteration. Sequenzen sind variable Zuweisungen, Auswahlen sind If-else-Entscheidungen und Iterationen sind Do-While-Schleifen. Dies sind die Bausteine der meisten heutigen Programmiersprachen und Paradigmen.

Objektorientierte Programmierung entfernt Zeiger auf Funktionen und führt Polymorphismus ein. PHP verwendet Zeiger nicht so wie C, aber eine Variante dieser Zeiger auf Funktionen kann in Variable-Funktionen beobachtet werden. Auf diese Weise kann ein Programmierer den Wert einer Variablen als Namen einer Funktion verwenden, sodass Folgendes erreicht werden kann:

Dies mag auf den ersten Blick nicht wichtig erscheinen. Aber denken Sie darüber nach, was wir mit einem so mächtigen Werkzeug erreichen können. Wir können eine Variable als Parameter an eine Funktion senden und diese Funktion dann die andere aufrufen lassen, auf die durch den Wert des Parameters verwiesen wird. Das ist großartig. Es ermöglicht uns, die Funktionalität einer Funktion zu ändern, ohne dass sie es weiß. Ohne dass die Funktion überhaupt einen Unterschied bemerkt.

Mit dieser Technik können wir auch polymorphe Aufrufe durchführen.

Anstatt darüber nachzudenken, welche Zeiger auf Funktionen bieten, sollten Sie jetzt darüber nachdenken, wie sie funktionieren. Sind sie nicht nur versteckte "go-to"-Anweisungen? Tatsächlich sind sie indirekten "go-to" s oder zumindest sehr ähnlich. Welches ist nicht sehr gut. Was wir hier haben, ist in der Tat eine clevere Möglichkeit, "go-to" zu machen, ohne es direkt zu verwenden. Ich muss zugeben, dass es in PHP, wie das obige Beispiel zeigt, ziemlich leicht zu verstehen ist, aber es kann mit größeren Projekten und vielen verschiedenen Funktionen, die von einer Funktion an eine andere übergeben werden, verwirrend werden. In C ist es noch dunkler und schrecklich schwer zu verstehen.

Es reicht jedoch nicht aus, nur Zeiger auf Funktionen zu entfernen. Objektorientierte Programmierung muss auf elegante Weise einen Ersatz bieten. Es bietet Polymorphismus mit einer einfachen Syntax. Und mit dem Polymorphismus kommt der größte Wert, den die objektorientierte Programmierung bietet: Der Steuerungsfluss ist der Abhängigkeit vom Quellcode entgegengesetzt.

dependency_comparisondependency_comparisondependency_comparison

Im obigen Bild haben wir ein einfaches Beispiel dafür dargestellt, wie polymorphe Aufrufe in den beiden verschiedenen Paradigmen auftreten. Bei der prozeduralen oder strukturellen Programmierung ähnelt der Kontrollfluss der Quellcode-Abhängigkeit. Beide weisen auf eine konkretere Umsetzung des Druckverhaltens hin.

Bei der objektorientierten Programmierung können wir die Quellcode-Abhängigkeit umkehren und auf die abstraktere Implementierung hinweisen, während der Kontrollfluss auf die konkretere Implementierung verweist. Dies ist wichtig, da wir möchten, dass unsere Kontrolle den bestmöglichen konkreten und flüchtigen Teil unseres Codes erreicht, damit wir unser Ergebnis genau so erhalten, wie wir es möchten, aber in unserem Quellcode wollen wir genau das Gegenteil. In unserem Quellcode möchten wir, dass das konkrete und flüchtige Material aus dem Weg geht, leicht zu ändern ist und den Rest unseres Codes so wenig wie möglich beeinflusst. Lassen Sie die flüchtigen Teile häufig wechseln, aber lassen Sie die abstrakteren Teile unverändert. Weitere Informationen zum Prinzip der Abhängigkeitsinversion finden Sie in der Original-Forschungsarbeit von Robert C. Martin.


Die momentane Aufgabe

In diesem Kapitel erstellen wir eine einfache Anwendung, um Google-Kalender und die darin enthaltenen Ereignisse aufzulisten. Zuerst werden wir einen prozeduralen Ansatz verfolgen, indem wir nur einfache Funktionen verwenden und jegliche Art von Klassen oder Objekten vermeiden. Mit der Anwendung können Sie Ihre Google-Kalender und -Ereignisse auflisten. Dann werden wir das Problem noch einen Schritt weiter gehen, indem wir unseren Verfahrenscode beibehalten und damit beginnen, ihn nach Verhalten zu organisieren. Schließlich werden wir es in eine objektorientierte Version umwandeln.


PHP Google API-Client

Google bietet einen API-Client für PHP. Wir werden es verwenden, um eine Verbindung zu unserem Google-Konto herzustellen, damit wir dort Kalender bearbeiten können. Wenn Sie den Code ausführen möchten, müssen Sie Ihr Google-Konto so einrichten, dass Kalenderabfragen akzeptiert werden.

Obwohl dies eine Voraussetzung für das Tutorial ist, ist es nicht das Hauptthema. Anstatt dass ich die Schritte wiederhole, die Sie unternehmen müssen, werde ich Sie auf die richtige Dokumentation verweisen. Keine Sorge, die Einrichtung ist sehr einfach und dauert nur etwa fünf Minuten.

Der PHP Google API-Clientcode ist in jedem Projekt aus dem Beispielcode enthalten, der diesem Tutorial beigefügt ist. Ich empfehle Ihnen, diese zu verwenden. Wenn Sie wissen möchten, wie Sie es selbst installieren können, schauen Sie in die offizielle Dokumentation.

Folgen Sie dann den Anweisungen und geben Sie die Informationen in die Datei apiAccess.php ein. Diese Datei wird sowohl für die prozeduralen als auch für die objektorientierten Beispiele benötigt, sodass Sie sie nicht wiederholen müssen. Ich habe meine Schlüssel dort gelassen, damit Sie Ihre leichter identifizieren und ausfüllen können.

Wenn Sie NetBeans verwenden, habe ich die Projektdateien in den Ordnern belassen, die die verschiedenen Beispiele enthalten. Auf diese Weise können Sie die Projekte einfach öffnen und sofort auf einem lokalen PHP-Server (PHP 5.4 ist erforderlich) ausführen, indem Sie einfach Ausführen/Projekt ausführen wählen.

Die Clientbibliothek zum Herstellen einer Verbindung mit der Google-API ist objektorientiert. Für unser Funktionsbeispiel habe ich eine kleine Reihe von Funktionen geschrieben, die die Funktionen enthalten, die wir benötigen. Auf diese Weise können wir eine prozedurale Ebene verwenden, die über die objektorientierte Clientbibliothek geschrieben ist, sodass unser Code keine Objekte verwenden muss.

Wenn Sie schnell testen möchten, ob Ihr Code und Ihre Verbindung zur Google-API funktionieren, verwenden Sie einfach den folgenden Code als index.php-Datei. Es sollte alle Kalender auflisten, die Sie in Ihrem Konto haben. Es sollte mindestens einen Kalender geben, wobei das summary-Feld Ihr Name ist. Wenn Sie einen Kalender mit den Geburtstagen Ihres Kontakts haben, funktioniert dieser möglicherweise nicht mit dieser Google-API, aber keine Panik. Wählen Sie einfach einen anderen aus.

Diese index.php-Datei ist der Einstiegspunkt in unsere Anwendung. Wir werden kein Webframework oder etwas Besonderes verwenden. Wir werden einfach einen HTML-Code ausgeben.


Ein direkter verfahrenstechnischer Ansatz

Nachdem wir nun wissen, was wir erstellen und was wir verwenden werden, laden Sie den angehängten Quellcode herunter. Ich werde Ausschnitte daraus bereitstellen, aber um das Ganze zu sehen, sollten Sie Zugriff auf die Originalquelle haben.

Für diesen Ansatz wollen wir nur die Dinge zum Laufen bringen. Unser Code wird sehr rudimentär organisiert sein, mit nur wenigen Dateien wie folgt:

  • index.php - die einzige Datei, auf die wir direkt vom Browser aus zugreifen und an die GET-Parameter übergeben werden.
  • functions_google_api.php - der Wrapper über die Google-API, über die wir oben gesprochen haben.
  • functions.php - wo alles passiert.

functions.php wird alles enthalten, was unsere Anwendung tut. Sowohl die Routing-Logik als auch die Präsentationen und die darin enthaltenen Werte und Verhaltensweisen sind möglicherweise vergraben. Diese Anwendung ist ziemlich einfach, die Hauptlogik lautet wie folgt.

procedural_schemaprocedural_schemaprocedural_schema

Wir haben eine einzelne Funktion namens doUserAction(), die mit einer langen if-else-Anweisung entscheidet, welche anderen Methoden basierend auf den Parametern in der GET-Variablen aufgerufen werden sollen. Die Methoden stellen dann über die API eine Verbindung zum Google-Kalender her und drucken alles, was wir anfordern möchten, auf dem Bildschirm aus.

Dieses Beispiel ist wahrscheinlich die komplizierteste Funktion in unserem Code. Es ruft eine Hilfsfunktion namens putTitle() auf, die nur formatierten HTML-Code für die Überschrift druckt. Der Titel enthält den Namen für unseren Kalender, den Sie durch Aufrufen von getCalendar() unter functions_google_api.php erhalten. Der zurückgegebene Kalender ist ein Array, das ein summary-Feld enthält. Das ist es, wonach wir suchen.

Die $client-Variable wird in allen unseren Funktionen überall übergeben. Es ist erforderlich, eine Verbindung zur Google-API herzustellen. Wir werden uns später darum kümmern.

Als Nächstes durchlaufen wir alle Ereignisse im aktuellen Kalender. Diese Liste von Arrays wird durch Ausführen des in retrieveEvents() gekapselten API-Aufrufs abgerufen. Für jedes Ereignis drucken wir das Erstellungsdatum und dann den Titel aus.

events_in_a_calendarevents_in_a_calendarevents_in_a_calendar

Der Rest des Codes ähnelt dem, was wir bereits besprochen haben, und ist noch einfacher zu verstehen. Fühlen Sie sich frei, damit herumzuspielen, bevor Sie mit dem nächsten Abschnitt fortfahren.


Organisation der Verfahrensordnung

Unser aktueller Code ist in Ordnung, aber ich denke, wir können es besser machen und ihn angemessener organisieren. Sie finden das Projekt mit dem vollständigen organisierten Code unter dem Namen "GoogleCalProceduralOrganized" im angehängten Quellcode.

Verwenden einer globalen Clientvariablen

Das erste, was mich an unserem unorganisierten Code ärgert, ist, dass wir diese $client-Variable überall als Argument übergeben, mehrere Ebenen tief in verschachtelten Funktionen. Die prozedurale Programmierung hat eine clevere Möglichkeit, diese globale Variable zu lösen. Da $client in index.php und im globalen Bereich definiert ist, müssen wir nur ändern, wie unsere Funktionen es verwenden. Anstatt einen $client-Parameter zu erwarten, können wir Folgendes verwenden:

Vergleichen Sie den aktuellen Code mit dem neu organisierten Code, um den Unterschied festzustellen. Anstatt $client als Parameter zu übergeben, haben wir in all unseren Funktionen den global $client verwendet und ihn als Parameter nur an die Google API-Funktionen übergeben. Technisch gesehen hätten sogar die Google API-Funktionen die $client-Variable aus dem globalen Bereich verwenden können, aber ich denke, es ist besser, die API so unabhängig wie möglich zu halten.

Präsentation von Logik trennen

Einige Funktionen dienen eindeutig nur zum Drucken von Dingen auf dem Bildschirm, andere zur Entscheidung, was zu tun ist, und einige sind ein bisschen von beidem. In diesem Fall ist es manchmal am besten, diese Funktionen für bestimmte Zwecke in eine eigene Datei zu verschieben. Wir beginnen mit den Funktionen, die nur zum Drucken von Dingen auf dem Bildschirm verwendet werden. Diese werden in eine Datei functions_display.php verschoben. Siehe sie unten.

Der Rest dieses Prozesses der Trennung unserer Präsentation von der Logik erfordert, dass wir den Präsentationsteil aus unseren Methoden extrahieren. Hier ist, wie wir es mit einer der Methoden gemacht haben.

Wir können deutlich sehen, dass alles, was in der if-Anweisung enthalten ist, nur Präsentationscode ist und der Rest Geschäftslogik. Anstelle einer umfangreichen Funktion, die alles erledigt, werden wir sie in mehrere Funktionen aufteilen:

Nach der Trennung ist die Geschäftslogik nun sehr einfach. Wir haben sogar eine kleine Methode extrahiert, um festzustellen, ob das Ereignis das aktuelle ist. Der gesamte Präsentationscode liegt nun in der Verantwortung einer Funktion namens putEvent($event), die sich in der Datei functions_display.php befindet:

Obwohl diese Methode nur Informationen anzeigt, müssen wir berücksichtigen, dass dies von fundiertem Wissen über die Struktur von $event abhängt. Aber das ist vorerst in Ordnung. Die übrigen Methoden wurden auf ähnliche Weise getrennt.

Eliminieren langer if-else-Anweisungen

Das Letzte, was mich an unserem aktuellen Code stört, ist die lange if-else-Anweisung in unserer Funktion doUserAction(), mit der entschieden wird, was für jede Aktion zu tun ist. Jetzt ist PHP ziemlich flexibel, wenn es um Metaprogrammierung geht (Aufrufen von Funktionen als Referenz). Mit diesem Trick können wir Funktionsnamen mit den Werten der Variablen $_GET korrelieren. Wir können also einen einzelnen action-Parameter in die Variable $_GET einfügen und den Wert daraus als Funktionsnamen verwenden.

Basierend auf diesem Ansatz wird unser Menü wie folgt erstellt:

Wie Sie wahrscheinlich sehen können, hat uns diese Umstrukturierung bereits zu einem objektorientierten Design geführt. Es ist nicht klar, welche Art von Objekten wir haben und mit welchem genauen Verhalten, aber wir haben hier und da einige Hinweise.

Wir haben Präsentationen, die von Datentypen aus der Geschäftslogik abhängen. Dies ähnelt der Abhängigkeitsinversion, über die wir im Einführungskapitel gesprochen haben. Der Fluss des Steuerelements verläuft immer noch von der Geschäftslogik zur Präsentation, aber die Quellcode-Abhängigkeit begann sich in eine umgekehrte Abhängigkeit zu verwandeln. Ich würde sagen, an diesem Punkt ist es eher eine bidirektionale Abhängigkeit.

Ein weiterer Hinweis auf ein objektorientiertes Design ist die kleine Meta-Programmierung, die wir gerade durchgeführt haben. Wir nennen eine Methode, von der wir nichts wissen. Es kann alles sein und es ist, als hätten wir es mit einem geringen Grad an Polymorphismus zu tun.

Abhängigkeitsanalyse

Für unseren aktuellen Code könnten wir ein Schema wie das folgende zeichnen, um die ersten Schritte durch unsere Anwendung zu veranschaulichen. Das Zeichnen aller Linien wäre zu kompliziert gewesen.

organized_procedural_schemaorganized_procedural_schemaorganized_procedural_schema

Wir haben mit blauen Linien markiert, die Prozedur ruft auf. Wie Sie sehen können, fließen sie in die gleiche Richtung wie zuvor. Zusätzlich haben wir die grünen Linien, die indirekte Anrufe markieren. Diese durchlaufen alle doUserAction(). Diese beiden Arten von Linien stellen den Kontrollfluss dar, und Sie können beobachten, dass er im Wesentlichen unverändert ist.

Die roten Linien führen jedoch ein anderes Konzept ein. Sie stellen eine rudimentäre Quellcode-Abhängigkeit dar. Ich meine rudimentär, weil es nicht so offensichtlich ist. Die putMenu()-Methode enthält die Namen der Funktionen, die für diesen bestimmten Link aufgerufen werden müssen. Dies ist eine Abhängigkeit und die gleiche Regel gilt für alle anderen Methoden zum Erstellen von Links. Sie hängen vom Verhalten der anderen Funktionen ab.

Eine zweite Art von Abhängigkeit ist auch hier zu sehen. Die Abhängigkeit von Daten. Ich habe zuvor $calendar und $event erwähnt. Die Druckfunktionen müssen über genaue Kenntnisse der internen Struktur dieser Arrays verfügen, um ihre Aufgaben ausführen zu können.

Nach all dem denke ich, dass wir viele Gründe haben, zu unserem letzten Schritt überzugehen.


Eine objektorientierte Lösung

Unabhängig vom verwendeten Paradigma gibt es keine perfekte Lösung für ein Problem. Hier ist also, wie ich vorschlage, unseren Code objektorientiert zu organisieren.

Erster Instinkt

Wir haben bereits begonnen, Bedenken in Geschäftslogik und Präsentation zu trennen. Wir haben sogar unsere doUserAction()-Methode als separate Einheit vorgestellt. Mein erster Instinkt ist also, drei Klassen zu erstellen: Presenter, Logic und Router. Diese werden sich höchstwahrscheinlich später ändern, aber wir brauchen einen Ausgangspunkt, oder?

Der Router enthält nur eine Methode und bleibt der vorherigen Implementierung ziemlich ähnlich.

Jetzt müssen wir unsere putMenu()-Methode explizit mit einem neuen Presenter-Objekt aufrufen, und der Rest der Aktionen wird mit einem Logic-Objekt aufgerufen. Dies verursacht jedoch sofort ein Problem. Wir haben eine Aktion, die nicht in der Logic-Klasse enthalten ist. putHome() gehört zur Presenter-Klasse. Wir müssen eine Aktion in Logic einführen, die an die putHome()-Methode des Präsentators delegiert wird. Denken Sie daran, dass wir unseren vorhandenen Code vorerst nur in die drei Klassen einschließen möchten, die wir als mögliche Kandidaten für ein OO-Design identifiziert haben. Wir wollen nur das tun, was unbedingt notwendig ist, damit das Design funktioniert. Nachdem wir den Arbeitscode haben, werden wir ihn weiter ändern.

Sobald wir eine putHome()-Methode in die Logic-Klasse einfügen, haben wir ein Dilemma. Wie rufe ich Methoden von Presenter auf? Nun, wir könnten ein Presenter-Objekt erstellen und an Logic übergeben, sodass es immer einen Verweis auf die Präsentation enthält. Lassen Sie uns das von unserem Router aus tun.

Jetzt können wir einen Konstruktor in Logic hinzufügen und die Delegierung in Richtung putHome() in Presenter hinzufügen.

Mit ein paar geringfügigen Anpassungen in index.php und Presenter, der die alten Anzeigemethoden umschließt, Logic, der die alten Geschäftslogikfunktionen umschließt, und Router, der den alten Aktionsselektor umschließt, können wir unseren Code tatsächlich ausführen und das Menüelement "Home" funktionieren lassen.

Und hier ist es in Aktion.

oo_home_workingoo_home_workingoo_home_working

Als nächstes müssen wir in unserer Logic-Klasse die Aufrufe ordnungsgemäß ändern, um die Logik anzuzeigen und mit $this->presenter zu arbeiten. Dann haben wir zwei Methoden - isCurrentEvent() und retrieveEvents() - die nur innerhalb der Logic-Klasse verwendet werden. Wir werden sie privat machen und die Anrufe entsprechend ändern.

Wir werden dann den gleichen Ansatz mit der Presenter-Klasse verfolgen. Wir werden alle Aufrufe von Methoden ändern, um auf $this->something zu verweisen und putTitle(), putLink() und putBlock() privat zu machen, da sie nur von Presenter verwendet werden. Überprüfen Sie den Code im Verzeichnis GoogleCalObjectOrientedInitial im angehängten Quellcode, wenn Sie Schwierigkeiten haben, alle diese Änderungen selbst vorzunehmen.

Zu diesem Zeitpunkt haben wir eine funktionierende App. Es handelt sich hauptsächlich um prozeduralen Code, der in die OO-Syntax eingeschlossen ist, die immer noch die globale Variable $client verwendet und Tonnen anderer antiobjektorientierter Gerüche aufweist, aber funktioniert.

Wenn wir das Klassendiagramm mit Abhängigkeiten für diesen Code zeichnen, sieht es folgendermaßen aus: >

oo_initial_class_diagramoo_initial_class_diagramoo_initial_class_diagram

Sowohl die Flusssteuerungs- als auch die Quellcode-Abhängigkeiten durchlaufen den Router, dann die Logik und schließlich die Präsentation. Diese letzte Änderung, die wir vorgenommen haben, verblasst tatsächlich ein wenig von der Abhängigkeitsinversion, die wir in unserem vorherigen Schritt beobachtet haben. Aber lass dich nicht täuschen. Das Prinzip ist da, wir müssen es nur offensichtlich machen.

Zurücksetzen der Quellcode-Abhängigkeit

Es ist schwer zu sagen, dass ein SOLID-Prinzip wichtiger ist als ein anderes, aber ich denke, dass das Prinzip der Abhängigkeitsinversion den größten unmittelbaren Einfluss auf Ihr Design hat. Dieses Prinzip besagt:

A: High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beide sollten von Abstraktionen abhängen und B: Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen.

Einfach ausgedrückt bedeutet dies, dass konkrete Implementierungen von abstrakten Klassen abhängen sollten. Je abstrakter Ihre Klassen werden, desto weniger ändern sie sich. Sie können das Problem also wie folgt wahrnehmen: Häufig wechselnde Klassen sollten von anderen, viel stabileren Klassen abhängen. Der volatilste Teil einer Anwendung ist wahrscheinlich die Benutzeroberfläche, bei der es sich um die Presenter-Klasse in unserer Anwendung handelt. Lassen Sie uns diese Abhängigkeitsinversion offensichtlich machen.

Zuerst wird unser Router nur den Presenter verwenden und seine Abhängigkeit von der Logik aufheben.

Dann ändern wir Presenter, um eine Instanz von Logic zu verwenden, und fragen ihn nach den Informationen, die er präsentieren muss. In unserem Fall halte ich es für akzeptabel, dass Presenter die Instanz von Logic tatsächlich erstellt. In jedem Produktionssystem werden jedoch wahrscheinlich Fabriken die Geschäftslogik-bezogenen Objekte erstellen und in die Präsentationsschicht einfügen.

Jetzt verschwindet die Funktion putHome(), die sowohl in der Logic- als auch in der Presenter-Klasse vorhanden ist, aus Logic. Dies ist ein gutes Zeichen, da wir Duplikate entfernen. Der Konstruktor und der Verweis auf Presenter verschwinden ebenfalls aus Logic. Andererseits muss ein Konstruktor, der ein Logikobjekt erstellt, in Presenter geschrieben werden.

Wenn Sie nach diesen Änderungen auf Kalender anzeigen klicken, wird jedoch ein netter Fehler angezeigt. Da alle unsere Aktionen innerhalb der Links auf Funktionsnamen in der Logic-Klasse verweisen, müssen wir einige konsistentere Änderungen vornehmen, um die Abhängigkeit zwischen den beiden umzukehren. Nehmen wir jeweils eine Methode. Die erste Fehlermeldung lautet:

Daher möchte unser Router eine Methode aufrufen, die in Presenter nicht vorhanden ist: printCalendars(). Lassen Sie uns diese Methode in Presenter erstellen und überprüfen, was sie in Logic getan hat. Es druckte einen Titel und ging dann einige Kalender durch und rief putCalendar() auf. In Presenter sieht die printCalendars()-Methode folgendermaßen aus:

Andererseits wird die Methode in Logic ziemlich anämisch. Nur ein Weiterleitungsaufruf an die Google API-Bibliothek.

Dies kann dazu führen, dass Sie sich zwei Fragen stellen: "Benötigen wir tatsächlich eine Logikklasse?" und "Hat unsere Anwendung überhaupt eine Logik?". Nun, wir wissen es noch nicht. Vorerst werden wir den obigen Prozess fortsetzen, bis der gesamte Code funktioniert und die Logik nicht mehr von Presenter abhängt.

Daher verwenden wir in Presenter eine printCalendarContents()-Methode wie die folgende:

Dies wiederum ermöglicht es uns, getEventsForCalendar() in Logic in etwa so zu vereinfachen.

Nun funktioniert das, aber ich habe hier ein Problem. Die Variable $_GET wird sowohl in der Logic- als auch in der Presenter-Klasse verwendet. Sollte nicht nur die Presenter-Klasse $_GET verwenden? Ich meine, Presenter muss unbedingt über $_GET Bescheid wissen, da er Links erstellen muss, die diese $_GET-Variable füllen. Das würde also bedeuten, dass $_GET streng HTTP-bezogen ist. Jetzt möchten wir, dass unser Code mit einer CLI oder einer grafischen Desktop-Benutzeroberfläche funktioniert. Daher möchten wir dieses Wissen nur im Presenter behalten. Dadurch verwandeln sich die beiden Methoden von oben in die beiden unten.

Die letzte Funktion, mit der wir uns befassen müssen, ist das Drucken eines bestimmten Ereignisses. Nehmen wir für dieses Beispiel an, es gibt keine Möglichkeit, ein Ereignis direkt abzurufen, und wir müssen es selbst finden. Jetzt ist unsere Logikklasse praktisch. Es ist ein perfekter Ort, um Ereignislisten zu bearbeiten und nach einer bestimmten ID zu suchen:

Und dann kümmert sich der entsprechende Aufruf von Presenter um das Drucken:

Das ist es. Hier sind wir. Abhängigkeit invertiert!

oo_dip_class_diagramoo_dip_class_diagramoo_dip_class_diagram

Die Steuerung fließt weiterhin von der Logik zum Präsentator. Der präsentierte Inhalt wird vollständig von Logic definiert. Wenn wir beispielsweise morgen eine Verbindung zu einem anderen Kalenderdienst herstellen möchten, können wir eine weitere Logik erstellen, diese in Presenter einfügen, und Presenter bemerkt nicht einmal einen Unterschied. Außerdem wurde die Quellcode-Abhängigkeit erfolgreich invertiert. Presenter ist der einzige, der Logik erstellt und direkt von dieser abhängt. Diese Abhängigkeit ist entscheidend, damit Presenter die Anzeige von Daten ändern kann, ohne dass dies Auswirkungen auf die Logik hat. Darüber hinaus können wir unseren HTML Presenter gegen einen CLI Presenter oder eine andere Methode zur Anzeige der Informationen für den Benutzer austauschen.

Die globale Variable loswerden

Wahrscheinlich ist der letzte verbleibende schwerwiegende Konstruktionsfehler die Verwendung einer globalen Variablen für $client. Der gesamte Code in unserer Anwendung hat Zugriff darauf. Wie durch ein Wunder ist die einzige Klasse, die tatsächlich $client benötigt, unsere Logic-Klasse. Der offensichtliche Schritt besteht darin, eine private Klassenvariable zu erstellen. Dazu müssen wir jedoch $client über den Router in den Presenter weitergeben, damit ein Logikobjekt mit der Variablen $client erstellt werden kann. Dies löst wenig von unseren Problemen. Wir müssen unsere Klassen an einem isolierten Ort erstellen und die Abhängigkeiten richtig ineinander einfügen.

Für jede größere Klassenstruktur würden wir Factories verwenden, aber für unser kleines Beispiel ist die Datei index.php ein großartiger Ort, um die Erstellungslogik zu speichern. Als Einstiegspunkt in unsere Anwendung, auch bekannt als "Haupt"-Datei im Architekturschema auf hoher Ebene, liegt sie immer noch außerhalb der Grenzen unserer Geschäftslogik.

HighLevelDesignHighLevelDesignHighLevelDesign

Also ändern wir index.php in den folgenden Code, wobei alle Includes und der Befehl session_start() beibehalten werden:


Abschließende Gedanken

Und wir sind fertig. Es gibt sicherlich noch einige andere Dinge, die wir tun könnten, um unser Design noch besser zu machen. Wenn nichts anderes, könnten wir ein paar Tests für unsere Methoden für die Logic-Klasse schreiben. Vielleicht könnte unsere Logic-Klasse auch in etwas Repräsentativeres wie GoogleCalendarGateway umbenannt werden. Oder wir könnten Ereignis- und Kalenderklassen erstellen, um die Daten und das Verhalten dieser Konzepte noch besser zu steuern und die Abhängigkeit des Präsentators von einem Array für diese Datentypen aufzuheben. Eine weitere Verbesserung und Erweiterung wäre, tatsächlich polymorphe Aktionsklassen zu erstellen, anstatt nur Funktionen als Referenz von $_GET aufzurufen. Es gibt endlose kleine Dinge, die wir tun könnten, um selbst dieses einfache Design weiter zu verbessern. Ich werde Ihnen diese großartige Gelegenheit zum Experimentieren geben, beginnend mit dieser endgültigen Version des Codes, den Sie im angehängten Archiv unter dem Verzeichnis GoogleCalObjectOrientedFinal finden.

Wenn Sie abenteuerlustig sind, können Sie diese kleine Anwendung erweitern, um eine Verbindung zu anderen Kalenderdiensten herzustellen und Informationen auf unterschiedliche Weise und auf unterschiedlichen Plattformen darzustellen. Für alle Benutzer von NetBeans enthält jeder Quellcodeordner ein NetBeans-Projekt, sodass Sie diese direkt öffnen können sollten. In der endgültigen Version ist PHPUnit auch für Sie vorbereitet, aber in den restlichen Projekten habe ich es entfernt, weil wir keine Tests durchgeführt haben.

Danke fürs Lesen.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.