dedlfix: PHP - OOP - Wie sollte ich am besten meinen Code strukturieren?

Beitrag lesen

Tach!

Anmerkung: Ich möchte keine vollwertige IDE verwenden, da mich diese mMn, ähnlich wie ein Framework von der Erlernung der Basis abhält und ich "Magie" vermeiden möchte. Auch auf die Erlernung einer neuen IDE möchte ich zunächst verzichten.

Ich denke, da überschätzt du, was eine IDE zu tun in der Lage ist. Sie verdeckt nichts, wie das ein Framework tut, wenn es eine Schicht auf die nativen Funktionalitäten legt. Sie schreibt dir auch nicht auf magische Weise deinen Code. Sie hilft nur enorm, indem sie Abhängigkeiten findet, dich mit einem Tastendruck zur Definition einer Variable oder Funktion bringt, und dir alle Verwendungen von Dingen aufzeigen kann. Sie kann auch Tipps geben, wie man im Detail besseren Code schreiben kann. Auch der Lernaufwand ist nun soo groß auch wieder nicht. Man muss ja nicht alle Funktionen bis ins kleinste Detail kennen. Es reicht, wenn man sie wie zunächst wie einen Texteditor verwendet und dann nach und nach nebenbei die Funktionen kenenlernt.

    - SortData.php
    - TransformData.php

Daten ist das, was man gemeinhin mit Computerprogrammen zu verarbeiten gedenkt. Versuch mal einen besseren Namen zufinden, der genauer bezeichnet, welche Daten da verarbeitet werden.

Mein Workflow ist im Moment sehr holprig. Ich teste die Ausgabe einer jeden Methode, indem ich die entsprechende Klasse in der index.php initialisiere und dann die Methode mit print_r ausgebe. Scheint mir recht Zeitaufwendig.

Testen ist zeitaufwendig, egal wie du es anstellst. Du kannst nur Zeit sparen, wenn du immer wieder dasselbe testen willst, dass du dir da Tests schreibst, die automatisch abgearbeitet werden können. Das verschlingt aber initial noch eine Menge Zeit mehr, aber du kannst dann sicher sein, dass zumindest für deine geschriebenen Testfälle sich die Komponenten korrekt verhalten. (TDD, hast du ja schon genannt.)

Die erste Hürde für mich war, wo ich denn eigentlich beginnen sollte. Ich hab mir gedacht das Lesen und Parsen der Konfigurationsdatei könnte ein guter Einstieg sein. Ich habe mich hier für das Singleton-Pattern entschieden, aus dem vielleicht naiven Grund, dass dieses Pattern verhindert, dass ich eine Klasse mehrfach initialisiere, so, dass nur ein Objekt exisitiert und, weil in den Beschreibungen stand, dass das Singleton-Pattern dafür geeignet ist Objekte global zur Verfügung zu stellen.

Wenn du ein Objekt instantiierst und dieses durchreichst, hast du auch nur ein Objekt.

Dependency-Injection kann ich noch nicht und TDD wollte ich mir nicht als zusätzliche Hürde auferlegen.

DI ist im Grunde genommen nur, dass man übergibt, womit die Funktionen und Objekte arbeiten sollen, statt dass sie sich die Daten selbst holen. Mehr nicht. Es gibt da noch DI-Frameworks, die beim Auflösen der Abhängigkeiten helfen, aber die kannst du für den Anfang auch unbeachtet lassen.

Ich hab die Tage schon mal die Clean Code Talks von Miško Hevery (5 Videos, glaub ich sind das) empfohlen, der beschreibt auch ein paar Aspekte guter Code-Strukturierung.

private function getConfigObject(){
    $configJson = file_get_contents($_SERVER['DOCUMENT_ROOT'] . "\config\config.json", false);
    $this->data = json_decode($configJson, true);
}

Das ist ein Teil, der das Prinzip "don't look for things" nicht einhält. Der Dateiname wäre ein Kandidat für DI. Bei einer Änderung muss man den nicht irgendwo im Code suchen, sondern hat ihn irgendwo zentral im Bereich der Anwendungsinitialisierung. Eine Konstante bietet sich sogar dafür an. Das ist auch der einzige Konfigurationswert, der literal notiert sein muss, die anderen stehen ja dann in der Datei.

private function __clone() {
}
 
private function __wakeup() {
}

Lass weg, was du nicht brauchst. YAGNI.

Ich kenne MVC nur oberflächlich, aber ich weiß, dass da an erster Stelle ein Router steht, der die einkommende Request verarbeitet und damit dann einen Controller aufruft. Also habe ich gedacht, es wäre eine gute Idee mit der Umsetzung eben dieses Routers fortzufahren.

Der Router gehört nicht zum MVC-Muster. Gleichwohl findet man ihn in MVC-Frameworks, weil er eine wichtige Hilfsfunktion für solchen Anwendungen bereitstellt. An erster Stelle aber steht üblicherweise ein Front-Controller, der Initialisierungsarbeiten vornimmt und der dann den Request zum Router schickt, zwecks Ermittlung des zu verwendenden MVC-Controllers.

Ich verwende aus oben genannten Gründen wieder das Singleton-Pattern [für den Router].

Was ist so wichtig daran, dass es nur eine Routerinstanz geben darf? Wird die überhaupt mehrfach von Teilen der Anwendung angefragt? Vermutlich wohl eher nicht. Dass etwas nur einmal verwendet wird, ist zum Beispiel keine stichhaltige Begründung für ein Singleton (abgesehen von den generellen Argumenten gegen Singletons).

private function __construct(){
    return $this->getUrlParams();
}

Konstruktoren haben keine Rückgabewerte. Es heißt immer, Konstruktoren erstellen Objekte, so dass man zu dem Schluss kommen kann, dann könne der Konstruktor ja auch mal was anderes erstellen. Aber __construct ist nicht der Konstruktor, sondern nur ein Teil. Es ist nur die Konstruktor-Funktion, eine vom Konstruktionsvorgang aufgerufene spezielle Funktion, in der der Programmierer seinen Code unterbringen kann. Sie wird aufgerufen, nachdem das Objekt grundlegend instantiiert wurde. Das, worauf $this zeigt, existiert bereits und wird ohne weiteres Zutun vom new-Operator zurückgegeben. - Du kannst diese Funktion weiterhin Konstruktor nennen, so wie das im Prinzip alle Programmierer machen, aber mit dem Wissen im Hintergrund, dass das nur eine nachgelagerte Funktion im Konstruktionsprozess ist.

private function getLanguage (){
    return $this->getGETParam('l');
}

private function getApplicationHash(){
    return $this->getGETParam('a');
}

private function getVersionHash(){
    return $this->getGETParam('v');
}

Für meinen Geschmack sind diese drei Funktionen entbehrlich. Gut, sie haben einen sprechenden Bezeichner, der mehr aussagt, als wenn man nur die Zeile im Funktionskörper sieht. Aber an der einzigen Stelle, an der sie aufgerufen werden, geht bereits recht gut hervor, wofür das l, a und v steht. Overengineering muss nämlich auch nicht sein.

Dann habe ich mir den ChangelogReader vorgenommen. Dieser liest alle im Ordner "data" enthaltenen XML Dateien und kettet sie aneinander. Auch hier habe ich wieder das Singleton-Pattern verwendet. Da ich dieses Array ja global zur Verfügung stellen muss, damit ich es anschließend gemäß des Konfigurationsobjektes, und den GET-Parametern transformieren, filtern und sortieren kann.

Dieses "muss" halte ich für ein Gerücht. Wenn Verwender den Reader brauchen, dann kann man ihnen den auch übergeben. Sie müssen ihn sich nicht selbst holen. Und im Falle von TDD kann man dann auch eine zum Testen besser geeignete Instanz übergeben, anstatt dass man eine mehr oder weniger komplette Programmumgebung nachbauen muss, in der sich der Testkandidat wohl fühlt.

Hier wurde ich zum ersten mal stutzig, ob das mit dem Singleton-Pattern wirklich so eine gute Idee ist. Ich hatte und habe dafür aber keine Argumente dagegen, außer, dass es sich nicht gut anfühlt dieses Pattern so häufig zu verwenden. Deshalb nachfolgend: Singleton die zum 3. Mal.

Und wieder grüßt das Singleton-Pattern:

Stattdessen könnte man auch gleich statische Klassen verwenden. Oder doch lieber DI.

Deshalb bitte ich um Euer Feedback zu der allgemeinen Vorgehens- und Denkweise, meinem Workflow, zu Variablen- und Klassennamen, zur Strukturierung des Projektes und vielleicht ein Tipp, wie ich weitermachen kann.

Probier mal, die DI-Idee zu verfolgen.

dedlfix.