Funktional oder Klassenbasierend bei Konfiguration & Initialisierung im PHP Framework?
MB
- php
- programmiertechnik
- projekt
moin Community,
hab die Konstruktion und parallel Konzeption eines MVC Frameworks angefrangen. Bin nur bei konfiguration und initialisierungen. Noch keine MVC Klassen, nich mal n Router.
Frage: Was sollte funktional und was objekt orientiert programmiert werden? Ich bin beide werge gegangen und will bei einem bleiben.
vlg MB
Eric Foster Johnson schreibt in seinem Buch über Klassenentwurf: Eine Klassenhierarchie wird vom Ende her, von der am weitesten abgeleiteten Klasse her entwickelt. Das heißt, man beginnt nicht mit einer Basisklasse und überlege sich die Erweiterungen sondern macht es genau umgekehrt. Bezogen auf ein Web-Application-Framework heißt das, die Entwicklung einer etwaigen Klassenhierarchie beginnt mit der Response.
Betrachte mal eine Response als Objekt, da gäbe es schon als erste Eigenschaft den Content-Type. Und im Fall, dass das text/html ist, fallen Dir bestimmt noch weitere Eigenschaften ein wie title, descr usw.
Hangel Dich mal so von unten nach oben durch, so findest Du auch die richtige Position wo die Routing-Table anzusiedeln ist. Auf diesem Weg wirst Du auch mit der Frage der Vererbung konfrontiert und natürlich auch mit Methoden die ein Response-Objekt haben muss. MfG
Tach!
Eric Foster Johnson schreibt in seinem Buch über Klassenentwurf: Eine Klassenhierarchie wird vom Ende her, von der am weitesten abgeleiteten Klasse her entwickelt. Das heißt, man beginnt nicht mit einer Basisklasse und überlege sich die Erweiterungen sondern macht es genau umgekehrt. Bezogen auf ein Web-Application-Framework heißt das, die Entwicklung einer etwaigen Klassenhierarchie beginnt mit der Response.
Ich verstehe nicht, warum sollte man mit der Response beginnen sollte? Die ist doch keine Struktur, die über allen anderen trohnt. Stattdessen sehe ich ein solches Framework bestehend aus vielen Dingen, die miteinander arbeiten und keinerlei hierarchische Berührungspunkte haben, die, wenn da Bedarf besteht, zu anderen nur als Parameter übergeben und/oder von ihnen als Ergebnis entgegengenommen werden. Man kann da an verschiedenen Stellen anfangen, ohne dass es Basisklassen werden. In einigen Fällen jedoch schreibt man als Frameworkersteller lediglich Basisklassen, beispielsweise beim Controller. Die eigentlichen End-Klassen schreibt der Verwender des Frameworks.
dedlfix.
moin dedlfix,
ich hab pl so verstanden das man von innen nach ausen entwickeln sollte. induktiv. Ich hab deduktiv angefangen erst das grobe dann da kleine oder war es anders rum??? egal. Den wenn pl das gemeint hat was ich verstanden habe, dann ist es meines erachtens - je nach dem wie man es betrachtet - eine Methode der herangehensweise die durch aus sinnvoll ist.
vlg MB
Tach!
ich hab pl so verstanden das man von innen nach ausen entwickeln sollte.
Du gehst von dem aus, was letztlich verwendet werden soll. Zumindest bei der Planung. Wenn da zwei oder mehr Klassen sind, die ähnliches tun, dann kann daraus eine Basisklasse entstehen. Zum Beispiel sind die Validatorregeln unterschiedlich in ihrem eigentlichen Verhalten, aber gleich in der Verwendung. Vielleicht haben sie eine gemeinsame Funktionalität, dann kann diese in eine Basisklasse ausgelagert werden. Vielleicht brauchen sie aber nur ein gemeinsames Interface, weil zum Beispiel isValid() die einzige Funktion ist, die sie haben müssen, die Ausführung aber immer unterschiedlich ist. Du planst jedenfalls erstmal diese Validatorregelklassen und lagerst dann die Gemeinsamkeiten in eine Basisklasse aus oder definierst das Interface, oder beides.
Soweit so sinnvoll. Das war es dann aber auch schon mit der Hierarchie an dieser Stelle. Es wird am Ende kein großes Gebilde entstehen, mit der Response an der Spitze, weswegen das Beginnen mit der Response keine für mich nachvollziehbare Schlussfolgerung daraus ist. Stattdessen werden eher viele kleine Hierarchien entstehen, wenn überhaupt die Notwendigkeit der Auslagerung in Basisklassen besteht. Wenn du das Prinzip der Trennung nach Zuständigkeiten berücksichtigst, arbeiten die Teile deines Frameworks relativ autonom und sind nicht auf Erbschaften angewiesen. Wenn sie mit anderen zusammenarbeiten, dann in Form der Übergabe als Parameter. Am Ende spielt die Hierarchieplanung eine untergeordnete Rolle, wenn es kaum Hierachien gibt.
dedlfix.
moin dedlfix,
ich hab pl so verstanden das man von innen nach ausen entwickeln sollte.
Du gehst von dem aus, was letztlich verwendet werden soll. Zumindest bei der Planung.
exakt.
Wenn da zwei oder mehr Klassen sind, die ähnliches tun, dann kann daraus eine Basisklasse entstehen.
Das werde ich später tun. Darauf möchte ich sehr gern zurück greifen!
Das war es dann aber auch schon mit der Hierarchie an dieser Stelle. Es wird am Ende kein großes Gebilde entstehen
ist ja auch nicht Mein Sinn der Sache.
Stattdessen werden eher viele kleine Hierarchien entstehen, wenn überhaupt die Notwendigkeit der Auslagerung in Basisklassen besteht. Wenn du das Prinzip der Trennung nach Zuständigkeiten berücksichtigst, arbeiten die Teile deines Frameworks relativ autonom und sind nicht auf Erbschaften angewiesen. Wenn sie mit anderen zusammenarbeiten, dann in Form der Übergabe als Parameter.
So auch meim Gedanke. Danke für die Bestärtigung :).
vlg MB
Tach!
Eric Foster Johnson schreibt in seinem Buch über Klassenentwurf:
Welches Buch wäre das? Wenn ich mir bei amazon.com die Resultatslist zu seinem Namen ansehe, finde ich keins, dass sich dem Titel nach mit Klassendesign beschäftigt. Vielleicht ist das auch in einem seiner anderen Bücher enthalten. Die allerdings haben alle schon 10 oder mehr Jahre auf dem Buckel. Klar, einige Grundlagen bleiben, aber manche Dinge von damals betrachtet man heutzutage mitunter mit anderen Augen.
dedlfix.
Tach!
Frage: Was sollte funktional und was objekt orientiert programmiert werden? Ich bin beide werge gegangen und will bei einem bleiben.
Man muss sich da nicht einschränken. Du wirst hauptsächlich objektorientiert arbeiten. Aber wenn sich herausstellt, dass eine bestimmte Aufgabe besser funktional zu lösen geht, dann nimm das da an der Stelle. Das wird sich vermutlich auf die Fälle beschränken, wenn du eine der PHP-Funktionen aufrufen möchtest, die eine Funktion übergeben haben möchte, um arbeiten zu können. Zum Beispiel beim Sortieren eines Arrays mit einer benutzerdefinierten Funktion.
Wenn du beispielsweise vor der Frage stehst, ob foreach oder array_map() zu verwenden sei, also iterativ oder funktional, dann kannst du das entscheiden, wie du gern magst. Vielleicht wirst du dabei Wege gehen, die sich früher oder später als ungünstig herausstellen. Passiert halt. Dann schreibt man es eben um. Ich würde mich jedenfalls nicht festlegen/einschränken wollen auf eine bestimmte Vorgehensweise und alles andere sozusagen tabuisieren.
dedlfix.
hi pl,
interessante, einleuchtende Vorgehensweise. wede ich beherzigen Danke Dir!
vlg MB
hi dedlflix ,
Vielleicht wirst du dabei Wege gehen, die sich früher oder später als ungünstig herausstellen. Passiert halt. Dann schreibt man es eben um.
danke für deinen Rat. Das also rumheulen und dann nicht alles in die Tonne kloppen sondern umschreiben. Ich schätze deine Erfahrung sehr.
vlg MB
Hallo MB,
Rumheulen ist beim Bau größerer Software eine übliche Beschäftigung. Dinge in die Tonne zu kloppen auch. Wichtig ist, trotzdem weiterzumachen.
Rolf
Grundlage für Zitat #2251.
Hallo MB,
ein richtiges PHP Großprojekt habe ich noch nicht gemacht - ich beschäftige mich "nur" mit der Umstellung eines Alt-Projektes auf OO-Standbeine. Das Ding ist von 2003 und sieht vom HTML und PHP entsprechend aus. Ich mache das seit 3 Jahren in meiner Freizeit, mit langen Auszeiten, nicht beruflich.
Mein Vorgehen war zunächst hybrid, d.h. ich habe den ganzen Rahmencode prozedural gelassen und mir Worker-Klassen geschrieben, die bestimmte Teilfunktionen abgebildet haben. D.h. ich habe bei Bedarf einen solchen Worker erzeugt und Methoden darauf genutzt.
Das hat sich ausgeweitet, bis ich irgendwann so weit war, den prozeduralen Teil weitgehend zu eliminieren (der war nämlich redundant in allen PHP Seiten drin). Jetzt habe ich eine Klasse für den Session-Handler, für den Router, eine Utility-Klasse für statischen Krimskrams, und eine "Context" Klasse, die letztlich den Rahmen für eine Request-Abarbeitung bildet. Mein "Hauptprogramm" ist noch ein klein wenig prozedural; ich erzeuge die Context-Klasse und stecke ihr ein paar Helper dazu (einen Logger, den Router, die SQL Access Factory und solche Dinge) und rufe dann nur noch sowas auf wie context->handleRequest().
Den ersten Nutzen von der Bausteintechnik hatte ich Anfang des Jahres. Das Projekt läuft noch mit mysql, und ich muss natürlich weg davon. Ich habe eine zweite SQL Factory geschrieben, die alle Zugriffe über PDO laufen lässt, und kann nun schrittweise die Repositories (also die Klassen, die DB-Zugriffe nach Fachdomäne bündeln) von der einen auf die andere SQL Factory umstellen. Bedeutet natürlich, dass ich nach Möglichkeit KEIN SQL selbst schreibe, sondern von der Factory erzeugen lassen. Nach Möglichkeit. Manchmal muss es eben doch...
Konfiguration steckt zur Zeit in einer settings.php, prozedural, weil noch nicht alle Seiten der Anwendung auf MVC umgestellt sind. Meine Context-Klasse kopiert die dort angelegten Werte in Instanzvariablen des Kontexts bzw. stellt sie den angeschlossenen Helpern zur Verfügung (z.B. DB-Credentials für die SQL Factory).
Die handleRequest Methode bedient sich des Routers, um zur URL den passenden Controller und die passende Action zu finden. Ich arbeite mit Convention-Mapping und unterstütze überhaupt keine Routing Configuration, d.h. wenn die URL einen Controller Foo und eine Aktion Bar beschreibt, suche ich im Controller-Ordner ein FooController.php, lade das, erzeuge den Controller und rufe darauf die Methode Bar() auf. Ein besseres Framework müsste mehrere Ordner kennen, in denen Controller liegen können, und ggf. einen flexibleren Parser für Routen. Bisher brauchte ich das nicht, mein Schema ist immer foo/controller/action/parameter1,parameter2,... - und foo ist mein Hauptprogramm foo.php, der Apache ruft das einfach auf, ohne URL Rewriting. D.h. meine Anwendung läuft als http://example.org/foo/controller/action/…
Wenn nach foo Schluss ist, habe ich einen Defaultcontroller (start), und wenn keine action angegeben ist, rufe ich die Defaultaction (index) auf. Und foo.php ist die Default-Seite des Web, d.h. rufst Du http://example.org auf, landest Du bei foo.php mit Controller=start und Action=index.
Viel zu konfigurieren ist da gar nicht :)
Wenn Du Dinge konfigurieren musst, dann schau Dir an, wie weit diese Konfigurationsdaten eine Datenstruktur bilden. Ist keine da, mach einfach ein Array als key->value Dictionary. Ist aber was da, dann bau es adäquat auf, als Array, mit Subobjekten, einfach so, dass es zur Struktur der Daten passt. Lass dich bei einer Liste von ähnlichen Werten nicht darauf ein, ein Array mit Schlüsseln wie "fup_1", "fup_2", "fup_3", "fup_count" zu bauen, sondern mach ein einziges Property "fup" und bau es als Array auf. Natürlich musst Du Dir überlegen, wie Du das in deiner Konfigurationsdatei hinterlegst - aber es spricht NICHTS dagegen, eine von Dir gepflegte Konfiguration als config.php zu schreiben und dort die benötigte Datenstruktur zusammenzubauen. Ein .ini-Format, wie die php.ini, ist nicht das Beste. Du kannst auch eine XML Darstellung wählen, ein JSON Format oder eine YAML Datei. Wird alles von PHP unterstützt.
Rolf
Moin RolfB,
ich beschäftige mich "nur" mit der Umstellung eines Alt-Projektes auf OO-Standbeine.
klingt aber auch sehr spannend
(z.B. DB-Credentials für die SQL Factory).
Was zur Hecke ist SQL Factory??? Ich hab schon probleme mit den Statements wie ich das lösen kann. Bin gespannt auf deine AW.
vlg MB
Hallo MB,
Die SQL Factory kümmert sich darum, unabhängig von der verwendeten Datenbank die SQL Statements zum DB Server zu bringen. PDO ist grundsätzlich schon so was. Meine Factory kann noch die verarbeiteten Statements loggen, und sich merken, was an einem Satz geändert wurde seit er gelesen wurde, oder ob er ganz neu ist, und dann ein geeignetes UPDATE oder ein INSERT generieren. Sie hat auch einen Readonly Mode zum sperren von Schreibzugriffen.
Einiges davon kann MySQL out-of-the-box, aber als ich das baute waren meine entsprechenden Kenntnisse mangelhaft. Und ich vermeide unstandardisierte Konstrukte eines DB Servers wann immer es geht.
Vermutlich könnte ich dieses Teil auch durch ein fertiges ORM Tool ersetzen.
Rolf