zippex: Grundsatzfragen zur Verwendung von Klassen

Hallo!
Ich bin gerade in den Anfängen eines größeren Projekts, dass möglichst modular aufgebaut sein soll. Also dachte ich mir, dass sich die Verwendung von Klassen anbietet.
Eine Klasse für die Datenbank mit connect(), getResult($query), executeQuery($query) etc.
Eine Klasse für die Benutzerverwaltung mit login($name, $pw), setUserdata(...), getUserdata(...) etc.

Mit diesen Überlegungen bin ich aber für mich bei einigen Grundsatzfragen angelangt. Bisher hatte ich immer eine funktionale Trennung, also eine Datei datenbank.php, in der alle Funktionen mit SQL-Anfragen für alle php-Dateien des Projekts drinstanden.

Mit den Klassen sollte ich das dann - so dachte ich mir zumindest - anders machen. Ist es (logisch) richtig, dass eine Klasse sämtlichen Code "von sich" enthält? Die Klasse "Benutzerverwaltung" beinhaltet also sowohl Datenbankfunktionen mit SQL-Anweisungen als auch z.B. "draw"-Methoden für die Ausgabe auf dem Bildschirm?

Ist es sinnvoll, eine Art System-Klasse à la Main-Methode zu schreiben, die sich um das Grobe kümmert? Also z.B. HTML-Code von <html> bis <body> generiert - oder dafür doch besser Templates?

Ist das logisch ok, wenn verschiedene Klassen auf Session-Variablen lesend und schreibend zugreifen? Oder sollte ich in die Session besser ein (z.B. Benutzer-)Objekt stecken, auf dass ich dann wieder nur über die entsprechende (Benutzer-)Klasse zugreifen kann?

Wenn ich so munter jeder Klasse ihre eigene "Draw"-Methode gebe, ist das dann noch mit einer globalen CSS-Datei vereinbar oder sollte ich die dann auch aufteilen?
Viele Grüße!
zippex :)

  1. Hallo,

    puh, damit wirfst Du hier ein Thema auf, das sehr umfangreich _und_ komplex ist, und über das schon jede Menge geschrieben wurde von Leuten, die weit mehr Ahnung haben als ich.

    Mit den Klassen sollte ich das dann - so dachte ich mir zumindest - anders machen. Ist es (logisch) richtig, dass eine Klasse sämtlichen Code "von sich" enthält? Die Klasse "Benutzerverwaltung" beinhaltet also sowohl Datenbankfunktionen mit SQL-Anweisungen als auch z.B. "draw"-Methoden für die Ausgabe auf dem Bildschirm?

    Eine allgemeingültige Formel dafür, wie Du Deine Klassen optimal aufbauen und wie fein Du dabei die Funktionsbereiche trennen mußt, gibt es nicht. Schau Dir am besten einmal an, wie andere Leute das machen. PHP-Klassen und -Klassenbibliotheken gibt es im Netz ja zuhauf (z.B. Stichwort "PEAR" -> http://pear.php.net/).
    Sinnvollerweise sorgt man aber dafür, daß eine Klasse wirklich nur das enthält, wofür sie da ist und worauf ihr Name hindeutet. Packt man alle Funktionalität in eine Klasse, so hat der objektorientierte Ansatz überhaupt keinen Vorteil mehr.

    Ist es sinnvoll, eine Art System-Klasse à la Main-Methode zu schreiben, die sich um das Grobe kümmert? Also z.B. HTML-Code von <html> bis <body> generiert - oder dafür doch besser Templates?

    Recherchiere mal nach dem Stichwort "Model View Controller" bzw. "MVC" und nach "Design Patterns"!

    Ciao,
    Andreas

    --
    "Das Corporate Design für das Internet sieht eine Reihe von Grafikelementen vor, die die Optik der Webseite visuell und funktionell beeinflussen." - (Zitat aus dem "Styleguide Corporate Design"  eines großen Konzerns...)
    1. Hi Leute

      Recherchiere mal nach dem Stichwort "Model View Controller" bzw. "MVC" und nach "Design Patterns"!

      Genau das Stichwort MVC hätte ich dir auch gegeben. Ich habe das mal folgendermassen angewandt:

      Für jedes Objekt, welches in der Datenbank abgelegt werden muss, wird eine Model-Klasse geschrieben, die die passenden Eigenschaften hat sowie die Methoden save(), load() und eventuell find().

      Controller sind die DB-Klassen (Connect, Query), Output-Template-Klasse und natürlich die sagenumwogene Business-Logic. Die Schnittstelle fällt je nach Verwendungszweck verschieden aus. Gleiches sollte aber dieselbe Schnittstelle haben, das ist ja klar. Funktioniert Vererbung in PHP? VBScript kann das leider nicht.

      Als Views habe ich zwei XHTML-Outputs (Übersicht (Tabelle) und Details), ein XHTML-Formular, sowie ein SVG-Output pro Model-Klasse implementiert. Alle Views haben dieselbe Schnittstelle und verwenden die Model-, Controller- und auch die View-Klassen um die Daten zu generieren.

      Das Resultat sind viele Klassen, noch mehr Files und das was du wolltest: Modularität.

      HTH & Peace

      Tom2

      1. Hallo!
        Danke für die Antworten soweit und sorry für meine etwas langsame Reaktion darauf - war im Kino. Doch zurück an die Arbeit, ich hab schon gesehen, dass dieses Thema öfters angesprochen wird :)  Ich sehe ein, dass die Idee der "draw"-Methoden zwar gedanklich vielleicht leichter, dafür aber nicht sinnvoll sind. Eine Aufteilung nach MVC würde dann wohl so aussehen:

        • Model: eine Abbildung meiner Datenbanktabellen als Objekte. Z.B. eine Klasse BENUTZER[username, password, email] mit entsprechenden set und get-Methoden (inkl. SQL-Querys).

        • View: Sämtliche darstellende Funktionen, z.B. für das Beispiel des Benutzers eine [Benutzername][Passwort][Button]-Kombination, aber auch Formulare zur Änderung von Benutzerdaten etc.

        • Controller: alles was Logik ist.
          Z.B.: Nicht eingeloggt? Rufe aus View-Komponente "drawLogin" auf. Vergleiche mit Datenobjekt aus DB. Wenn ok, nächster Schritt...

        Model und View sind quasi "dumme" Lieferanten und Arbeiter, Controller macht die ganze Arbeit. Schade, dass es das nicht bei Programmieren gibt, ich wäre jetzt gerne Model und View ;)

        Ich werde mich mal daran machen und das umsetzen. Beim Weg vom Kino nach Hause ist mir aber noch was eingefallen...
        Angenommen ich springe von einer Datei (a.php) zur anderen (b.php). Dann habe ich doch alle meine in a.php erzeugten Objekt-Instanzen verloren? Also nutze ich die $_SESSION als Objekt-Ablage? Ist das legitim?
        Beispiel:

        • Jemand loggt sich auf "a.php" ein, also passiert
                $benutzerObj = new Benutzer();
        • Benutzer möchte seine Daten ändern, die Controler-Komponente veranlasst also, dass ein Formular zum Ändern der Benutzerdaten angezeigt wird
        • Controllerkomponente macht zusätzlich
                $_SESSION['benutzerdaten'] = $benutzerObj;
        • Benutzer füllt das aus und schickt es ab (per POST)
        • POST-Daten werden an z.B. "b.php" geschickt, dort weiß der Controler (woher auch immer???), dass er folgendes zu tun hat:
             $benutzerObj = new Benutzer();
             $benutzerObj = $_SESSION['benutzerdaten'];
             $benutzerObj->setData($_POST[...]...);
             $benutzerObj->saveData();

        Klappt dieser Gedankengang? Objekte in der SESSION "parken", damit ich die die Zustände nicht verliere? Geht das überhaupt? Werd das glaub mal schnell testen, aber vielleicht weiß jemand von euch Vor- und Nachteile?
        Viele Grüße
        zippex :)

        1. Ich werde mich mal daran machen und das umsetzen. Beim Weg vom Kino nach Hause ist mir aber noch was eingefallen...
          Angenommen ich springe von einer Datei (a.php) zur anderen (b.php). Dann habe ich doch alle meine in a.php erzeugten Objekt-Instanzen verloren? Also nutze ich die $_SESSION als Objekt-Ablage? Ist das legitim?

          Man muss wohl nur lange genug suchen... das hier dürfte die Antwort für php4 sein:
          http://de3.php.net/manual/de/language.oop.serialization.php

          1. echo $begrüßung;

            Angenommen ich springe von einer Datei (a.php) zur anderen (b.php). Dann habe ich doch alle meine in a.php erzeugten Objekt-Instanzen verloren? Also nutze ich die $_SESSION als Objekt-Ablage? Ist das legitim?

            Ja, das geht. Die Frage ist nur, ob das auch sinnvoll ist. Doch die kann man nicht beantworten ohne konkrete Aufgabenstellung und ohne einen bestimmten Lösungsweg im Auge zu haben. Variablen vom Typ Resource lassen sich beispielsweise nicht in einer Session unterbringen und auch nicht serialisieren.

            Man muss wohl nur lange genug suchen... das hier dürfte die Antwort für php4 sein:
            http://de3.php.net/manual/de/language.oop.serialization.php

            Um das Serialisieren und Deserialisieren deiner Variablen (inkl. Objekte) brauchst du dich nicht zu kümmern, wenn du PHPs Sessionmechanismus verwendest. Das macht PHP von selbst.

            echo "$verabschiedung $name";

            1. Hello,

              Ja, das geht. Die Frage ist nur, ob das auch sinnvoll ist.

              Ich halte es nicht für sinnvoll.
              Unterschiedliche Scripte bedeutet doch, dass unterschiedliche Aufgaben erledigt werden sollen.
              Da erscheint mir eine neue Initialisierung und Instanziierung der Klasse sogar ratsam. Es gibt dann wahrscheinlich sogar unterschiwdliche Startbedingungen...

              Gleiche oder ähnliche Aufgaben packt man dann in ein Script, dass sich ja beliebig seine "Schlauheit" dazuladen kann. Nur den Prototyp der Klasse wird man wohl nicht wieder los, wenn man ihn einmal "included"  hat. Oder kann man Prototypen (auch Funktionen ) in PHP5 jetzt endlich auch wieder entladen, um den Speicher wieder freizugeben?

              Man muss wohl nur lange genug suchen... das hier dürfte die Antwort für php4 sein:
              http://de3.php.net/manual/de/language.oop.serialization.php

              Um das Serialisieren und Deserialisieren deiner Variablen (inkl. Objekte) brauchst du dich nicht zu kümmern, wenn du PHPs Sessionmechanismus verwendest. Das macht PHP von selbst.

              Leider gibt es da immer noch nicht die basisorientierten Funktionen "lose", damit man sich den Sessionmechanismus auch mit HTTP-AUTH (statt mit Cookie) zunutze machen könnte.

              Die vorhandenen passen nicht zusammen, bzw. passen nicht zum Session-Dateiformat.

              Harzliche Grüße vom Berg
              http://www.annerschbarrich.de

              Tom

              --
              Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
              Nur selber lernen macht schlau

              1. Hallo!
                Danke für die weiteren Antworten. Ich hab inzwischen gemerkt, dass die entsprechenden Klassen für die Objekte per include geladen werden müssen, bevor session_start() aufgerufen wird - sonst erkennt php wohl die Objekte in der Session nicht mehr. Also

                include('user-class.php);
                session_start();
                $userObj = new User();
                $userObj = $_SESSION['ablegter_user'];

                scheint zu klappen. Ich bin inzwischen wieder ein bisschen am zweifeln, ob eine durchgehendes Vorgehen mit Objekten sinnvoll ist. Paradebeispiel User:
                Die Klasse User enthält auch die Funktionen
                * getUser (gibt mit die Werte der internen Variablen _username, _passwort, ...)
                * setUser (ermöglicht mir das Ändern von Daten im Objekt).
                Um das in das Datenbank zu speichern, braucht die Userklasse dann aber auch eine private Funktion
                * loadUser(lädt User aus DB in interne Variablen) und
                * saveUser(schreibt interne Variablen in Datenbank).

                Das sind vier Funktionen, wo mir "früher" doch einfach zwei gereicht haben (getUserFromDb liefert direkt Array der Datenbank-Inhalte, saveUsertoDb speichert übergebene Parameter direkt wieder in der Datenbank).

                Diesen Mehraufwand fand ich noch einigermaßen vertretbar. Aber was mache ich, wenn ich jetzt nicht nur einen, sondern eine Liste von Usern brauche. Eine Funktion "getAllUsers" wäre in der Klasse "User" wohl ziemlich deplatziert, denn die Klasse "User" ist nur einer. Also bräuchte ich eine Klasse "UserList", die mit über getUserList ein Array von Benutzerobjekten zurückgibt. Puh, da fängt es dann schon an, sehr umständlich zu werden.
                Früher hab ich da einfach die oben genannte getUserFromDb-Funktion benutzt, die mir z.B. über einen Parameter $id=null ALLE Benutzer zurückliefert und über $id=17 nur Benutzer 17. Der Aufwand für konsequente Klassen wächst zusehens.

                Ich wollte gestern eigentlich schon mit programmieren anfangen, hab dann aber doch den ganzen Abend lang gegrübelt. Eine "Mischform" erscheint mir am einfachsten, bei der ich "Dinge die etwas machen" (z.B. Funktionen für Login und Zugriffe auf die Datenbank) in eine Klasse stecke. Ein Datenbank-Objekt benutze ich dann, wenn ich irgendwas von der Datenbank will.
                Wenn ich jedoch nur mit Daten arbeite (z.B. die oben beschriebenen User-Daten), dann ist das zu viel Aufwand, alles in Objekten zu kapseln. $db->getResult gibt mir auch ein Array von Benutzer-Arrays, wenn die dann noch assoziativ sind, habe ich quasi genau den gleichen leichten Zugriff wie über Objekte. Ein Ergebnis-Array wäre also
                [0]["username" => "karl", "password" => ".."]
                [1]["username" => "egon", "password" => ".."]
                [2]["username" => "bert", "password" => ".."]
                bei der User-Liste, und nur
                [0]["username" => "ken", "password" => ".."]
                wenn ich nach einem bestimmten User gesucht habe.

                Anders gesagt: ich will nur etwas in Klassen stecken, wenn viele "Subfunktionen" nötig sind, die mich aber eigentlich nicht interessieren. Z.B. könnte ich mir auch bei einer Dateiverwaltung von hochgeladenen Dateien vorstellen, dass ich das in eine Klasse stecke. Über $filemanager->readDir($dir), $filenamager->upload($file) etc. hätte ich dann "bequemen" Zugriff, was da im Hintergrund alles passiert interessiert mich nicht.

                Wie gesagt, wenn von der Klasse etwas "gemacht" wird, dann finde ich den Einsatz logisch. Wenn eine Klasse nur Abbild von Daten ist, die ich auch auf einfacherem Wege aus der DB bekomme, dann sehe ich da nicht so den Sinn.
                Und bei späteren Erweiterungen/Änderungen? Wird später z.B. der User noch mit "Adresse" erweitert, dann gibt es im assoziativen Rückgabearray einen neuen Schlüssel "Adresse". Funktionen/Module, die User-Daten benutzen und "Adresse" nicht kennen, stört das auch nicht und Funktionen/Module die "Adresse" brauchen, können sie nutzen.
                Gute Idee (allgemein)?
                Gute Idee (für ein Projekt, bei dem ich nur ein paar Wochen Zeit habe)?
                Viele Grüße
                zippex :)

                1. Hello,

                  scheint zu klappen. Ich bin inzwischen wieder ein bisschen am zweifeln, ob eine durchgehendes Vorgehen mit Objekten sinnvoll ist.

                  Das halte ich in PHP bis heute noch nicht für sinnvoll.

                  Das einzige, was ich an der OOP von PHP gegenüber der linearen Programmierung schätze, ist die Möglichkeit der Abgrenzung von Namespaces und das Nutzen privater Funktionen.

                  Letztlich ist das ganze Scriptkonzept des Webs in seiner Art doch schon objektorientiert. Durch das "Ping Pong" zwischen Server und Client kann es auch nicht passieren, dass z.B. ein List-Modul sich innerhalb seiner Lebensdauer selber aufruft.

                  Gemeint ist hier dass es keinen Stack für logisch aufeinanderfolgenden Abläufen gibt, sowie man das bei den klassischen Single-Maschine-Lösungen kennt.

                  Workflow muss man bei der Clent Server Programmierung anders umsetzen.

                  Harzliche Grüße vom Berg
                  http://www.annerschbarrich.de

                  Tom

                  --
                  Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                  Nur selber lernen macht schlau

                2. echo $begrüßung;

                  Danke für die weiteren Antworten. Ich hab inzwischen gemerkt, dass die entsprechenden Klassen für die Objekte per include geladen werden müssen, bevor session_start() aufgerufen wird - sonst erkennt php wohl die Objekte in der Session nicht mehr.

                  Ob includiert werden _muss_ oder nicht hängt davon ab, wie sich dein Code in verschiedenen Dateien aufteilt. Es muss nur die Definition der Klasse bekannt sein. Ob der Code dazu includiert wird oder im Script steht ist dabei irrelevant.

                  Also
                  include('user-class.php);
                  session_start();
                  $userObj = new User();
                  $userObj = $_SESSION['ablegter_user'];

                  Wenn nicht noch irgendwelche Seiteneffekte im Konstruktor stattfinden ist der Konstruktoraufruf nicht erforderlich. Die Objektvariablen werden sowieso durch das Lesen der Sessiondaten überschrieben.

                  Ich bin inzwischen wieder ein bisschen am zweifeln, ob eine durchgehendes Vorgehen mit Objekten sinnvoll ist. Paradebeispiel User:
                  Die Klasse User enthält auch die Funktionen
                  * getUser (gibt mit die Werte der internen Variablen _username, _passwort, ...)
                  * setUser (ermöglicht mir das Ändern von Daten im Objekt).
                  Um das in das Datenbank zu speichern, braucht die Userklasse dann aber auch eine private Funktion
                  * loadUser(lädt User aus DB in interne Variablen) und
                  * saveUser(schreibt interne Variablen in Datenbank).

                  Ja, das ist die Frage. Soll sich die Kuh selber melken oder soll sich die Milch selbst aus der Kuh melken?

                  Diesen Mehraufwand fand ich noch einigermaßen vertretbar.

                  PHP ist eigentlich ausgelegt, um auf einfache Art und Weise Webseiten zusammenzubauen. Natürlich kann man damit versuchen so zu programmieren, wie es unter beispielsweise C# üblich/notwendig ist. Was man unter PHP einfach mit einem Array macht, macht man dort vielleicht mit einer Collection. Natürlich kann man sich die Collection unter PHP nachbauen, aber entspricht das noch der Philosophie von PHP?

                  Aber was mache ich, wenn ich jetzt nicht nur einen, sondern eine Liste von Usern brauche. Eine Funktion "getAllUsers" wäre in der Klasse "User" wohl ziemlich deplatziert, denn die Klasse "User" ist nur einer. Also bräuchte ich eine Klasse "UserList", die mit über getUserList ein Array von Benutzerobjekten zurückgibt.

                  Vielleicht brauchst du auch ein (oder mehrere) Datenbankobjekte, die Methoden haben, um einen User oder ein Array mit allen Usern zurückzuliefern. Vielleicht helfen dir die Ansätze von PEARsDB_DataObject oder DB_Table weiter.

                  Puh, da fängt es dann schon an, sehr umständlich zu werden.

                  Ich kann dir nicht sagen, wie du am besten zu deinem Ziel kommst, da ich dieses nicht kenne. Was dir hilft wäre Erfahrung, und die bekommt man auch, indem man mal in die falsche Richtung läuft. Dann weiß man, dass dieser Weg für bestimmte Sachen ungeeignet ist, sich vielleicht aber für andere Dinge eignet.

                  echo "$verabschiedung $name";

                  1. Was dir hilft wäre Erfahrung, und die bekommt man auch, indem man mal in die falsche Richtung läuft.

                    *grins* ok, dann gehe ich mal los. Dankeschön für die vielen Wegweiser!
                    Viele Grüße
                    zippex :)

                3. Hallo zippex,

                  Aber was mache ich, wenn ich jetzt nicht nur einen, sondern eine Liste von Usern brauche. Eine Funktion "getAllUsers" wäre in der Klasse "User" wohl ziemlich deplatziert, denn die Klasse "User" ist nur einer. Also bräuchte ich eine Klasse "UserList", die mit über getUserList ein Array von Benutzerobjekten zurückgibt. Puh, da fängt es dann schon an, sehr umständlich zu werden.

                  Du hast dann einmal dein Objekt und einen Objekt-Handler.
                  Der ObektHandler holt dann anhand deiner Bedürfnisse entsprechende Benutzer aus der Datenbank und baut dann, wie du schon selbst geschrieben hast, ein Array mit Objekten zusammen. Da der Handler den gleichen Namen wie deine Klasse hat (eben nur mit dem Zusatz Handler), wird es nicht so schnell unübersichtlich, wie es sich auf den "ersten Ton" anhört.

                  Schönen Gruß aus München

                  die knappschaft

                  --
                  sh:(  fo:|  ch:?  rl:°  br:$,<,>  n4:&  ie:|  mo:|  va:)  de:]  zu:)  fl:|  ss:|  ls:[,#
                  1. Hallo!
                    Guter Hinweis, übersichtlich bleibt es auf diese Art und Weise dann doch. Ich werd noch mal drüber nachdenken. Danke für den Tipp.
                    Viele Grüße
                    zippex

                4. Moin!

                  Wie gesagt, wenn von der Klasse etwas "gemacht" wird, dann finde ich den Einsatz logisch. Wenn eine Klasse nur Abbild von Daten ist, die ich auch auf einfacherem Wege aus der DB bekomme, dann sehe ich da nicht so den Sinn.

                  Ich habe einen Webshop geschrieben, der auch Klassen benutzt. Einige waren fertige Projekte, z.B. die Klassen für Templates und Mailversand. Der eigentliche Shop sind meine eigenen Klassen.

                  Ein Shop braucht natürlich Zugriff auf eine Datenbank. Also fing ich mit einer Klasse "DB" an, welche die Verbindungsdaten zur Datenbank erhält und sämtliche Zugriffe auf die Datenbank kapselt. Nirgendwo außerhalb der DB-Klasse sollte auch nur das kleinste Fitzelchen SQL stehen.

                  Außerdem braucht ein Shop natürlich auch Sessions, um z.B. den Warenkorb zu speichern. Also schrieb ich noch eine Klasse "Session", welche sämtliche Funktionen enthält, die auf $_SESSION zugreifen. Nirgendwo außerhalb dieser Klasse sollte Zugriff auf $_SESSION genommen werden.

                  Und das allererste Problem, auf das ich stieß: Die Session-Klasse benötigt irgendwie Zugriff auf die Datenbank - wie soll das gehen?

                  Ich habe mich dann entschieden, dass die Session-Klasse die DB-Klasse erweitern (extends), also alle DB-Funktionen erbt, und die eigentlichen Shop-Skripte dann jeweils nur die Session-Klasse instanziieren. Ich habe also am Ende ein sehr monolithisches Shop-Objekt, was alles kann (Session- und DB-Zugriff).

                  Dieses Objekt speichere ich aber absichtlich nicht in der Session ab, weil es nichts bringen würde. Der einzige Sinn, ein Objekt in der Session zu speichern wäre, um bestimmte Variablen im Objekt zu retten und weiterzuverwenden. Sämtlicher Rest, nämlich der ganze Programmcode der Klasse, muß in jedem Skript immer wieder neu includiert und ausgeführt werden. Und auch die Verbindung zur Datenbank kann ja nicht per Session gerettet werden. Da der Shop auf PHP4 basiert, gibts auch keine "Rettungsfunktionen", die die DB-Verbindung vor dem Serialisieren schließen und nach dem Deserialisieren neu herstellen können.

                  Die strenge Trennung von Zugriffen (kein SQL außerhalb der DB-Klasse, kein Zugriff auf $_SESSION außerhalb der Session-Klasse - das sind zwei getrennte Include-Dateien) erleichtern die Wartung des Shops erheblich. Es ist für den gesamten restlichen Shop vollkommen egal, wie die Datenbank aufgebaut ist (da hat sich im Laufe der Zeit einiges verändert), solange die DB-Methoden zur Abfrage immer die gleichen Ergebnisse ausliefern. Veränderungen in der Datenbank lassen sich so sehr leicht kapseln.

                  Das Gleiche gilt für $_SESSION. Hier war ich zwar nicht ganz so konsequent und habe ein paar Lesezugriffe außerhalb der Klasse zugelassen, aber im Prinzip erlaubt diese Kapselung, dass die Session komplett eigenständig über die ideale Speicherung der notwendigen Warenkorb- und Kundendaten entscheidet, und lediglich das einmal in der Methode definierte Rückgabeformat einhalten muß. Erweiterungen im Rückgabeformat erzwingen eine neue Methode - die alte Methode muß kompatibel zu sich selbst bleiben, egal wie sie das macht (also entweder den alten Code beibehalten, oder z.B. die neue Methode aufrufen, und überflüssige Daten wegwerfen, um das erwartete Datenformat zurückgeben zu können.

                  Ich würde meinen Ansatz aber nicht wirklich objektorientiert nennen. Ich habe irgendwie das Gefühl, man muß in PHP nicht bis ins letzte mit Objekten arbeiten - zumal man davon nicht wirklich etwas hat, weil alle Objekte bei jedem Skriptstart wieder komplett neu hergestellt werden müsse - sie bleiben ja nicht von selbst im Speicher. Die Klassen dienen nur zur Kapselung der Funktionen in einem eigenen Namensraum, damit nicht unerwartet irgendwelche Konflikte auftreten. Die Vorgehensweise ist dabei sicherlich noch verbesserungswürdig - allerdings habe ich in den ganzen Jahren, die ich den Shop jetzt weiterentwickle, noch keinen Drang verspürt, nochmal komplett neu anzufangen. Alle bisherigen Ausbauten waren mit dem bisherigen Konzept prima verein- und realisierbar.

                  - Sven Rautenberg

                  --
                  My sssignature, my preciousssss!
                  1. Ein Shop braucht natürlich Zugriff auf eine Datenbank. Also fing ich mit einer Klasse "DB" an, welche die Verbindungsdaten zur Datenbank erhält und sämtliche Zugriffe auf die Datenbank kapselt. Nirgendwo außerhalb der DB-Klasse sollte auch nur das kleinste Fitzelchen SQL stehen.

                    Nun ist so ein Entwurf gut? Außer bei sehr kleinen Dingen würde ich sagen, nein.
                    Aus den Daten in der Datenbank wird man ja tpyischerweise ein Datenmodell aus Objekten aufbauen. Je komplexer die Anwendung wird, desto mehr Komponenten wird es geben, die irgendwie ein Datenmodell aus Daten in der Datenbank aufbauen.
                    Die gesammte Information, wie diese Modelle aufgebaut werden, wird in der Datenbank-Klasse stecken. Jede Änderung an einem Datenmodell wird eine Änderung an dieser Klasse bedeuten d.h. diese Anwendung vereint Logik von Komponenten, die ansonsten pratkisch nicht gekoppelt sind.

                    Ich habe bei solchen Anwendungen typischerweise eine Klasse, die die Verbindung zur Datenbank herstellt, und sehr viele Klassen, die diese benutzen und auf die Datenbank zugreifen.
                    Das wirft natürlich das Problem auf, dass die Datenbankzugriffe irgendwie abgestimmt sein müssen.
                    Das kann man entweder lösen, indem man jeder Klasse ihre eigenen Tabellen zuordnet und die Komponenten sind dann nur auf Anwendungsebene verknüpft. Die andere Möglichkeit ist, dass man einen Großteil diese "Modellerzeugungsschicht" direkt in der Datenbank implementiert.
                    Wenn man mit MySQL (speziell mit älteren Versionen) auskommen muss, kann man Letzteres nicht umsetzen, auch wenn es die bessere Lösung ist.

                    Grüße

                    Daniel

                    1. Moin!

                      Ein Shop braucht natürlich Zugriff auf eine Datenbank. Also fing ich mit einer Klasse "DB" an, welche die Verbindungsdaten zur Datenbank erhält und sämtliche Zugriffe auf die Datenbank kapselt. Nirgendwo außerhalb der DB-Klasse sollte auch nur das kleinste Fitzelchen SQL stehen.
                      Nun ist so ein Entwurf gut? Außer bei sehr kleinen Dingen würde ich sagen, nein.

                      Das hängt davon ab, was man will, würde ich meinen.

                      Objekte zu haben ist nett. Allerdings wäre es in meinem Shop darauf hinausgelaufen, dass ist anstelle von Arrays mit Strings Objekte mit Strings gehabt hätte. Also eine zusätzliche Ebene der Komplexität integriert hätte, die mir keinen Vorteil gebracht hätte.

                      Denn man weiß ja auch: Direkte Zugriffe auf die Eigenschaften eines Objektes sind böse, man benutze dafür besser get- und set-Methoden. Ich hätte also ein allgemeines "Ich verwalte ein Array"-Objekt gestrickt, welches mit getArrayValue($key) und setArrayValue($key,$value) anzusprechen wäre. Das ergibt keinen Vorteil gegenüber der Methode, einfach direkt ein Array zu verwenden.

                      Abgesehen davon war ich zu dem Zeitpunkt noch nicht so weit, komplexe objektorientierte Programmierung realisieren zu können. ;)

                      Aus den Daten in der Datenbank wird man ja tpyischerweise ein Datenmodell aus Objekten aufbauen. Je komplexer die Anwendung wird, desto mehr Komponenten wird es geben, die irgendwie ein Datenmodell aus Daten in der Datenbank aufbauen.

                      Objektorientierung hin oder her - solange man die Datenbank nur mit SQL ansprechen kann, muß jegliches Datenbankmodell immer wieder auf einen String zurückgeführt werden. Und aus einem String heraus wieder als Objekt generiert werden.

                      Ich habe bei solchen Anwendungen typischerweise eine Klasse, die die Verbindung zur Datenbank herstellt, und sehr viele Klassen, die diese benutzen und auf die Datenbank zugreifen.
                      Das wirft natürlich das Problem auf, dass die Datenbankzugriffe irgendwie abgestimmt sein müssen.

                      Das wirft vor allem die Problematik auf: Wie entstehen die Datenobjekte eigentlich. Ohne existierende DB-Zugriffsklasse funktionieren sie nicht. Wie erhalten sie Kenntnis von und Zugriff auf diese DB-Klasse?

                      Abgesehen davon haben eigentlich alle Datensätze einer Datenbank irgendwie etwas miteinander zu tun. Man kann nicht einfach so trennen. Bzw. wenn man trennt, dann eigentlich entlang der natürlichen DB-Grenzen: Datensätze und Tabellen.

                      Und da die Objekte sowieso alle am Skriptende wieder gelöscht werden: Warum nicht einfach genau die Daten aus der DB abfragen, die man braucht, und alle anderen ignorieren. Bei einem komplexen Objektmodell für Daten hätte ich das Gefühl, dass sich PHP dann die meiste Zeit mit dem Parsen der Klassendefinitionen rumschlägt, um dann nur genau eines der vielen möglichen Objekte wirklich anzulegen, mit Daten zu befüllen, in ein Template zu packen und sie auszugeben. Sowas kann man auch effizienter lösen. :)

                      Abgesehen davon: Meine Templateklasse kann ganz prima komplette Arrays entgegennehmen und direkt den Platzhaltern zuweisen - ideal bei Datenbankabfragen, bei denen die Feldbezeichner ja als Arrayschlüssel schon enthalten sind und durch Alias-Namen auch frei und fix gewählt werden können. Mit Objekten funktioniert das allerdings nicht. :)

                      - Sven Rautenberg

                      --
                      My sssignature, my preciousssss!
                      1. Hallo Sven,

                        Objektorientierung hin oder her - solange man die Datenbank nur mit SQL ansprechen kann, muß jegliches Datenbankmodell immer wieder auf einen String zurückgeführt werden. Und aus einem String heraus wieder als Objekt generiert werden.

                        Im Printip werden aus Tabellen Klassen und aus Datensätzen Objekte. Beziehungen dazwischen werden zu Referenzen. Wenn man natürlich die Antwort eines SQL-Queries direkt in HTML-Ausgabe und Formular-Daten wieder direkt in Update- und Insert-Operationen, dann ist es natürlich kaum sinnvoll, so etwas zu tun. Sinnvoll ist es wohl eher, wenn man so ein Datenmodell für aufwendigere Berechnungen/Transformationen benötigt und es auch nicht nach jedem Request neu aus der Datenbank erzeugen muss sondern durch irgendwelche Session-Mechanismen weiterreichen kann.

                        Das wirft vor allem die Problematik auf: Wie entstehen die Datenobjekte eigentlich. Ohne existierende DB-Zugriffsklasse funktionieren sie nicht. Wie erhalten sie Kenntnis von und Zugriff auf diese DB-Klasse?

                        Entweder kennen sie eben alle diese Zentrale Klasse und können eine Instanz davon erzeugen (typischerweise per Singelton) oder sie müssen bei der Instanzierung eben eine Instanz bekommen.

                        Abgesehen davon haben eigentlich alle Datensätze einer Datenbank irgendwie etwas miteinander zu tun. Man kann nicht einfach so trennen. Bzw. wenn man trennt, dann eigentlich entlang der natürlichen DB-Grenzen: Datensätze und Tabellen.

                        Eigentlich nur Anhand der Tabellen. Eine Komponente A darf eben die Datenbank nicht derart beeinflussen, dass eine Komponente B, die nichts von A weiß, von diesen Änderungen beeinflusst wird. Damit dürfen sich die Komponenten nicht gegenseitig in Tabellen schreiben und Abhänigkeiten zwischen den Tabellen bedeuten auch Abhängigkeiten der Komponenten.

                        Und da die Objekte sowieso alle am Skriptende wieder gelöscht werden: Warum nicht einfach genau die Daten aus der DB abfragen, die man braucht, und alle anderen ignorieren.

                        Das ist natürlich ein Problem. So flexibel Daten auswählen wie mit direktem Datenbankzugriff wird man nie können, wenn man ein Objektmodell dazwischen hat. Allerdings ist der Sinn eines solchen Modells ja auch zu abstrahieren. Man muss sich also eben überlegen, welche Zusammenstellungen von Daten man braucht. Man muss ja keine Modell für alle Daten erstellen.

                        Bei einem komplexen Objektmodell für Daten hätte ich das Gefühl, dass sich PHP dann die meiste Zeit mit dem Parsen der Klassendefinitionen rumschlägt.

                        Dass da jedes mal Scripte geparst werden, ist ein Problem von PHP bzw. auch nur mancher Möglichkeiten PHP auszuführen. Das kann man ja auch im Speicher halten. Bei CGI-Anwendungen u.ä. passiert das natürlich nicht, bei anderen Technologien schon.

                        um dann nur genau eines der vielen möglichen Objekte wirklich anzulegen

                        Das sollte wie gesagt auch nicht bei jedem Request passieren. Die Objekte müssen dann natürlich im Speicher gehalten oder serialisiert und deserialisiert werden.

                        mit Daten zu befüllen, in ein Template zu packen und sie auszugeben.

                        Ja, wenn zwischen Datenbankabfrage und Template praktisch nichts passiert, ist das wie schon gesagt kaum sinnvoll. Eine Shop-Anwendung tut ja aber mehr. Da Werten zumindest Artikel in einen Warenkorb gepackt, möglicherweise kann man Artikel irgendwie konfigurieren etc. Man hat also schon einiges an Information, was nicht in der Datenbank gespeichert wird bzw. erst dann, wenn der Kauf abgeschlossen ist.

                        Abgesehen davon: Meine Templateklasse kann ganz prima komplette Arrays entgegennehmen und direkt den Platzhaltern zuweisen - ideal bei Datenbankabfragen, bei denen die Feldbezeichner ja als Arrayschlüssel schon enthalten sind und durch Alias-Namen auch frei und fix gewählt werden können. Mit Objekten funktioniert das allerdings nicht. :)

                        Das ist nur eine Frage der Schnittstellen der Objekte. Ein Abfragen aller Eigenschaftsnamen und deren Werte ist da recht trivial umzusetzen.

                        Grüße

                        Daniel

  2. Hello,

    eigentlich würde ich nur gerne diesen Thread abonnieren, das heißt, immer eine Nachricht erhalten, wenn sich hier was tut, egal ob bei Auftuf des Forums oder per eMail...

    Aber das leistet das Forum wohl noch nicht, oder?
    Soll keine Kritik sein, sondern nur ein eben gerade entstandener Wunsch ;-)

    Harzliche Grüße vom Berg
    http://www.annerschbarrich.de

    Tom

    --
    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
    Nur selber lernen macht schlau

    1. Hallo Tom.

      eigentlich würde ich nur gerne diesen Thread abonnieren, das heißt, immer eine Nachricht erhalten, wenn sich hier was tut, egal ob bei Auftuf des Forums oder per eMail...

      Aber das leistet das Forum wohl noch nicht, oder?

      Natürlich. Schon seit Ewigkeiten.
      Du hast die Wahl:

      RSS
        • Atom
        • Mail (Siehe Vorschau)

      Einen schönen Dienstag noch.

      Gruß, Ashura

      --
      sh:( fo:} ch:? rl:( br: n4:~ ie:{ mo:| va:) de:> zu:} fl:( ss:) ls:[ js:|
      „It is required that HTML be a common language between all platforms. This implies no device-specific markup, or anything which requires control over fonts or colors, for example. This is in keeping with the SGML ideal.“
      [HTML Design Constraints: Logical Markup]
  3. Hallo zippex,

    wie schon gesagt wurde, ist dieses Thema beliebig komplex. Bloß ein paar Hinweise, die mir spontan einfallen:

    Versuche, die Klassen zu entkoppeln, also möglichst wenig Abhängigkeiten zu haben. Stell Dir vor, Du änderst eine Methode und darfst dann mal eben fünf, sechs Klassen ändern, damit es wieder passt.

    Eine Klasse = eine (!) Aufgabe. Weiß z. B. die Userklasse, dass es einen Bildschirm gibt? Kennt Sie auch das Ausgabeformat (html, xml, svg, ...), die Sprache, das Layout? Wenn ja, wozu? Was hat das mit Usern zu tun?

    Habe keine Angst vor vielen kleinen Klassen.

    Wenn Du Interfaces hast (z.B. php5 oder Java), mach Gebrauch von Ihnen. So kannst Du auf Schnittstellen und nicht auf Implementierungen zugreifen.

    Nicht leicht und erst sinnvoll, wenn Du schon mal Interfaces, abstrakte Klassen und Vererbung rudimentär verstanden hast:
    Das Buch „Head First Design Patterns“ (englisch oder deutsch) vom OReilly Verlag.

    Ach ja, auch Skizzen mit schönen Rechtecken, Pfeilen etc. helfen beim Entwickeln. Wer es standardkonform mag, schaut sich UML an.

    Gruß
    Olaf