Tom: PHP OOP Klassenaufteilung

0 101

PHP OOP Klassenaufteilung

Tom
  • php
  1. 0
    Jeena Paradies
    1. 0
      jobo
      1. 0
        Malcolm Beck´s
        1. 0
          jobo
          1. 0

            PHP Zend-Framewok Installation nutzen

            Malcolm Beck´s
            1. 0
              Jeena Paradies
              1. 0
                Malcolm Beck´s
                1. 0
                  Jeena Paradies
                  1. 0
                    Malcolm Beck´s
                    1. 0
                      Jeena Paradies
                    2. 0
                      jobo
                2. 0
                  jobo
                  1. 0
                    Malcolm Beck´s
                    1. 0
                      Jeena Paradies
                      1. 0
                        Tom
                        1. 0
                          Jeena Paradies
                          1. 0
                            Tom
                            1. 0
                              Jeena Paradies
                              1. 0
                                Tom
                                1. 0
                                  Jeena Paradies
                                  1. 0
                                    dedlfix
                                    1. 0
                                      Jeena Paradies
                                      1. 0
                                        dedlfix
                                        1. 0
                                          Jeena Paradies
                                2. 0
                                  Sven Rautenberg
                              2. 0
                                Malcolm Beck´s
                                1. 0
                                  Jeena Paradies
                                  1. 0
                                    Malcolm Beck´s
                            2. 0
                              Malcolm Beck´s
                              1. 2
                                Sven Rautenberg
                                1. 0
                                  Malcolm Beck´s
                                  1. 0
                                    Sven Rautenberg
                                    1. 0
                                      Malcolm Beck´s
                                      1. 8
                                        Sven Rautenberg
                                        1. 0
                                          Malcolm Beck´s
                                        2. 2
                                          dedlfix
                                        3. 0

                                          PHP OOP, wie eine Klassenhierarchie entsteht

                                          Tom
                                        4. 0
                                          Malcolm Beck´s
                                          1. 0
                                            Jeena Paradies
                                            1. 1
                                              dedlfix
                                            2. 1
                                              Sven Rautenberg
                                              1. 0
                                                Jeena Paradies
                                          2. 1
                                            dedlfix
                                            1. 0
                                              Sven Rautenberg
                                              1. 0
                                                dedlfix
                                          3. 2
                                            Sven Rautenberg
                                            1. 0
                                              dedlfix
                                              1. 0
                                                Sven Rautenberg
                                                1. 0
                                                  dedlfix
                                              2. 0
                                                Tom
                                                1. 0
                                                  dedlfix
                                            2. 0
                                              Malcolm Beck´s
                                              1. 0
                                                Sven Rautenberg
                                                1. 0
                                                  Claudius L.
                                                  1. 0
                                                    Claudius L.
                                                  2. 1
                                                    dedlfix
                                                    1. 0
                                                      Claudius L.
                                                      1. 2
                                                        Sven Rautenberg
                                                  3. 0
                                                    Sven Rautenberg
                                                2. 0
                                                  Malcolm Beck´s
                            3. 0
                              Sven Rautenberg
                3. 1
                  Matti Mäkitalo
                  1. 0
                    Malcolm Beck´s
                4. 0
                  Vinzenz Mai
                  1. 0
                    Malcolm Beck´s
                    1. 0
                      jobo
        2. 0
          dedlfix
          1. 0
            Malcolm Beck´s
            1. 0
              Malcolm Beck´s
              1. 1

                Uralt-Software

                Kai345
                • meinung
                1. 0
                  Malcolm Beck´s
                  1. 0
                    Kai345
                    1. 0
                      Malcolm Beck´s
            2. 0
              dedlfix
      2. 0
        Jeena Paradies
  2. 2
    Sven Rautenberg
    1. 0
      Tom
  3. 0
    hotti
    1. 0
      dedlfix
      1. 0
        Jeena Paradies
        1. 2
          dedlfix
          1. 0
            Jeena Paradies
      2. 0
        hotti
        1. 0
          Jeena Paradies
          1. 0
            Tom
            1. 0
              Jeena Paradies
              1. 0
                dedlfix
                1. 0
                  Jeena Paradies
                  1. 0
                    Sven Rautenberg
                    1. 0
                      Jeena Paradies
                      1. 0
                        dedlfix
                2. 0
                  Tom
                  1. 0
                    dedlfix
            2. 0
              Sven Rautenberg
        2. 2
          dedlfix
        3. 0
          hotti
      3. 0
        Matti Mäkitalo
        1. 0
          dedlfix
  4. 1
    jobo
  5. 0
    hotti

Hello,

iihr wisst ja, dass ich gerne auf OOP in PHP verzichte. Leider muss ich dann doch öfter mal was abliefern. Das nächste Projekt könnte nun etwas größer werden. OOP ist vorgeschrieben.

Ich habe da nun eine generelle Frage zur Philosophie. Es gibt in vielen Modulen ja immer die Qual, dass man sowohl Funktionalitäten für das Frontend, als auch Administrationsfunktionen für das Backend bereitstellen muss.

Wie trennt Ihr das? Gefordert ist, dass die Funktionen alle in einem Modul (einer Klasse) zusammengefasst werden, mit Ausnahme natürlich von Darstellungs-, Datenhaltungs-, oder Berechtigungsaufgaben, usw., die sich selbstverständlich in anderen Klassen befinden.

Ich möchte nicht immer den gesamten Klotz laden lassen, obwohl doch meistens nur die Userfunktionen benötigt werden.

Liebe Grüße aus dem schönen Oberharz

Tom vom Berg

--
 ☻_
/▌
/ \ Nur selber lernen macht schlau
http://bergpost.annerschbarrich.de
  1. Hallo,

    iihr wisst ja, dass ich gerne auf OOP in PHP verzichte.

    Warum eigentlich?

    Ich habe da nun eine generelle Frage zur Philosophie. Es gibt in vielen Modulen ja immer die Qual, dass man sowohl Funktionalitäten für das Frontend, als auch Administrationsfunktionen für das Backend bereitstellen muss.

    Wie trennt Ihr das? Gefordert ist, dass die Funktionen alle in einem Modul (einer Klasse) zusammengefasst werden, mit Ausnahme natürlich von Darstellungs-, Datenhaltungs-, oder Berechtigungsaufgaben, usw., die sich selbstverständlich in anderen Klassen befinden.

    Ich würde mir an deiner Stelle MVC angucken, vor allem für Webprojekte ist das eine sehr schöne Aufteilung und mittlerweile wohl auch der Standardweg (Web-)Applikationen zu designen.

    Wahrscheinlich würde ich mir sogar das Zend-Framework dazu mit angucken, oder gleich was schöneres wie Rails oder Django.

    Jeena

    1. Hallo,

      Wahrscheinlich würde ich mir sogar das Zend-Framework dazu mit angucken, oder gleich was schöneres wie Rails oder Django.

      Ist das schöner? Würde mich mal interessieren, warum. Bin jetzt kein Zend-Framework Fan, aber da ich das nicht professionell mache, bin ich da hängen geblieben, weil ich das alles sehr vernünftig fand:

      http://framework.zend.com/manual/de/zend.layout.quickstart.html

      Ich dachte, viel "schöner" (;-) gehts nicht.

      Gruß

      jobo

      1. مرحبا

        http://framework.zend.com/manual/de/zend.layout.quickstart.html
        Ich dachte, viel "schöner" (;-) gehts nicht.

        Wie ist das eigentlich mit dem ZF, wenn ich es einsetzen will, muss ich das Seperat installierem oder ist ZF mit PHP direkt verfügbar?
        Ich habe bisher mit keiner PHP-Installation zutun gehabt, wo ZF nicht integriert war (phpinfo()).

        mfg

        1. Hallo,

          مرحبا

          http://framework.zend.com/manual/de/zend.layout.quickstart.html
          Ich dachte, viel "schöner" (;-) gehts nicht.

          Wie ist das eigentlich mit dem ZF, wenn ich es einsetzen will, muss ich das Seperat installierem oder ist ZF mit PHP direkt verfügbar?
          Ich habe bisher mit keiner PHP-Installation zutun gehabt, wo ZF nicht integriert war (phpinfo()).

          Es ist ein Ordner mit alle den Klassen. Den kannst Du Dir runterladen und auf Deinem lokalen Xampp ausprobieren. Du kannst auch nur die Klassen nehmen, die Du brauchst, ähnlich wie bei PEAR. In dem Sinne ist das keine "Installation".

          Gruß

          jobo

          1. مرحبا

            Es ist ein Ordner mit alle den Klassen. Den kannst Du Dir runterladen und auf Deinem lokalen Xampp ausprobieren. Du kannst auch nur die Klassen nehmen, die Du brauchst,

            Warum eigentlich noch Seperat runterladen? Es ist doch in PHP bereits installiert? Zumindest bei mir steht in phpinfo überall ZF, warum kann ich diese Installation nicht direkt benutzen?

            mfg

            1. Hallo,

              Warum eigentlich noch Seperat runterladen? Es ist doch in PHP bereits installiert? Zumindest bei mir steht in phpinfo überall ZF, warum kann ich diese Installation nicht direkt benutzen?

              Weil es ein Webframework ist das dir Helferfunktionen für Webapplikationen bietet und von vorn herein eine sinnvolle Model View Controller Struktur mit in dein Projekt bringt. Es ist also so zu sagen ein Sceleton das viel initiale Arbeit die man bei jedem Projekt sonst von Hand machen muss abnimmt.

              Es ist halt zufällig in PHP geschrieben, hätte aber auch in JavaScript oder Haskell geschrieben sein können. Ist der Unterschied zwischen einer Sprache wie PHP oder Ruby und einem Framework wie ZF oder Rails klar?

              Jeena

              1. مرحبا

                Warum eigentlich noch Seperat runterladen?
                Weil es ein Webframework ist das dir Helferfunktionen für Webapplikationen bietet ...

                Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                mfg

                1. Hallo,

                  Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                  Hehe das ist als ob du fragen würdest warum man mit der OmBoard-Installation WordPress nicht nutzen kann.

                  Jeena

                  1. مرحبا

                    Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?
                    Hehe das ist als ob du fragen würdest warum man mit der OmBoard-Installation WordPress nicht nutzen kann.

                    In phpinfo() steht nichts von Wordpress, sehr wohl aber was vom Zend-Framework, sogar dass das ZF genutzt wird, also nicht nur installiert ist, sondern auch genutzt wird (also laufen muss).

                    Warum etwas doppelt installieren, wenn es schon auf der Kiste ist?

                    mfg

                    1. Hallo,

                      In phpinfo() steht nichts von Wordpress, sehr wohl aber was vom Zend-Framework, sogar dass das ZF genutzt wird, also nicht nur installiert ist, sondern auch genutzt wird (also laufen muss).
                      Warum etwas doppelt installieren, wenn es schon auf der Kiste ist?

                      unter OnBoard-Installation steht nix von Zend Framework, nur von Zend Engine und Zend Scripting.

                      Jeena

                    2. Hallo,

                      In phpinfo() steht nichts von Wordpress, sehr wohl aber was vom Zend-Framework, sogar dass das ZF genutzt wird, also nicht nur installiert ist, sondern auch genutzt wird (also laufen muss).

                      Warum etwas doppelt installieren, wenn es schon auf der Kiste ist?

                      Ich habe einen Ordner "library", darin "Zend" und darin viele Unterordner und Dateien, wie zB. View.php. Die müsstest Du irgendwo bei Dir auch haben ...;

                      Gruß

                      jobo

                2. Hallo,

                  مرحبا

                  Warum eigentlich noch Seperat runterladen?
                  Weil es ein Webframework ist das dir Helferfunktionen für Webapplikationen bietet ...

                  Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                  Wenn Du kannst, dann kannst Du. Dann hast Du aber irgendwo ein Verzeichnis mit all dem ZF-Libs. Ich hatte das bei meiner letzten Apache/Xampp-Installation glaube ich nicht dabei.

                  Gruß

                  jobo

                  1. مرحبا

                    Wenn Du kannst, dann kannst Du. Dann hast Du aber irgendwo ein Verzeichnis mit all dem ZF-Libs. Ich hatte das bei meiner letzten Apache/Xampp-Installation glaube ich nicht dabei.

                    Ich habe es anscheinend nur verwechselt.

                    Ich werde mir das ganze doch mal wieder genauer ansehen.

                    Danke allen für die Hinweise hier!

                    mfg

                    1. Hallo,

                      Ich werde mir das ganze doch mal wieder genauer ansehen.

                      Sehr gut! Ich befürchte dass der OP es sich nicht angucken wird, lasse mich aber gerne vom Gegenteil überzeugen.

                      Jeena

                      1. Hello R.,

                        Ich werde mir das ganze doch mal wieder genauer ansehen.
                        Sehr gut! Ich befürchte dass der OP es sich nicht angucken wird, lasse mich aber gerne vom Gegenteil überzeugen.

                        Ich habe das gelesen. Und ich schreib mir alles auf :-P

                        Liebe Grüße aus dem schönen Oberharz

                        Tom vom Berg

                        --
                         ☻_
                        /▌
                        / \ Nur selber lernen macht schlau
                        http://bergpost.annerschbarrich.de
                        1. Hallo,

                          Ich habe das gelesen. Und ich schreib mir alles auf :-P

                          Sehr gut, fehlt nur noch der Schritt der Anwendung des neu gelernten ;-)

                          Jeena

                          1. Hello,

                            Ich habe das gelesen. Und ich schreib mir alles auf :-P
                            Sehr gut, fehlt nur noch der Schritt der Anwendung des neu gelernten ;-)

                            Für den wissenschaftlichen Anteil an der Frage ist es fraglos richtig, sich auch mit Zend Framework zu beschäftigen.

                            Da es hier aber um den praktisch umsetzbaren Teil in einem bestehenden Projekt handelt, wird das leider ausscheiden. In ein älteres Projekt wegen ein paar gewünschter Erweiterungen nun ein Framework einzupflanzen erscheint mir einfach erheblich überskaliert. Das würde auch nicht bezahlt werden...

                            Ich muss versuchen, die Neuerungen so gut es geht nach MVC-Pattern zu bauen, ohne dass es ausartet. Und die neuen Klassen müssen äußerst unkompliziert dann später durch Andere verwendbar sein.

                            "Autoload" war da schon das richtige Stichwort. Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.

                            Liebe Grüße aus dem schönen Oberharz

                            Tom vom Berg

                            --
                             ☻_
                            /▌
                            / \ Nur selber lernen macht schlau
                            http://bergpost.annerschbarrich.de
                            1. Hallo,

                              Da es hier aber um den praktisch umsetzbaren Teil in einem bestehenden Projekt handelt, wird das leider ausscheiden. In ein älteres Projekt wegen ein paar gewünschter Erweiterungen nun ein Framework einzupflanzen erscheint mir einfach erheblich überskaliert. Das würde auch nicht bezahlt werden...

                              Ich habe nirgendwo mitbekommen dass es schon alten Code gibt der weiterverwendet werden soll.

                              Ich muss versuchen, die Neuerungen so gut es geht nach MVC-Pattern zu bauen, ohne dass es ausartet. Und die neuen Klassen müssen äußerst unkompliziert dann später durch Andere verwendbar sein.

                              Das ist ja der Sinn von Abstraktion wenn man es richtig macht.

                              Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.

                              Hm mir ist nicht ganz klar wie man das nicht sinnvoll entscheiden könnte, folgt nicht automatisch aus der Art des Objekts ob es von einem anderen erbt oder nicht? Ein Ford wird wohl von Auto die meisten Attribute erben, aber eine Ente wohl kaum.

                              Jeena

                              1. Hello,

                                Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.
                                Hm mir ist nicht ganz klar wie man das nicht sinnvoll entscheiden könnte, folgt nicht automatisch aus der Art des Objekts ob es von einem anderen erbt oder nicht? Ein Ford wird wohl von Auto die meisten Attribute erben, aber eine Ente wohl kaum.

                                Machs doch mal an einem echten Webbeispiel fest und nicht an diesen hinkenden Enten miot Zweizylindermotor.

                                Simples Beispiel: Gästebuch mit Kommentarfunktion und Bildupload-Möglichkeit.

                                Vom Webdesigner soll nachher nur eine neue Klasse "guestbook" instantiiert und eingebunden werden und die Darstellungsmethode aufgerufen werden. Alles andere muss automatisch ablaufen.

                                Wie teilst Du nun die Klassen auf?

                                Aufgaben:
                                (Authentifizierung) ist schon vorhanden und muss mitgenutzt werden
                                (Berechtigungen)    sit schon vorhanden und muss "irgendwie" mitgenutzt werden
                                Detail-Darstellung für Alle, satzweise oder seitenweise
                                Listendarstellung mit Kurzdaten
                                Neueintrag
                                Editierungsmöglichkeit (mit Cookie) innerhalb 24 Stunden
                                Freigabe/Sperre (Admin)
                                Kommentar (durch Alle)

                                usw.

                                Kannst Dir gerne noch mehr ausdenken.

                                Vorgabe:
                                Es sollen nur die Klassen geladen werden, die für das jeweilige Dokument am Browser erforderlich sind.

                                Liebe Grüße aus dem schönen Oberharz

                                Tom vom Berg

                                --
                                 ☻_
                                /▌
                                / \ Nur selber lernen macht schlau
                                http://bergpost.annerschbarrich.de
                                1. Hallo,

                                  (Authentifizierung) ist schon vorhanden und muss mitgenutzt werden

                                  Da würde ich ne klasse Authentification machen mit methoden wie

                                  void authenticate($user, $password)
                                  bool isAuthenticated();
                                  array errors();

                                  $user wäre ein Objekt der Klasse User die dann einen solchen User repräsentiert mit sachen wie:

                                  • username
                                  • email
                                  • firsname
                                  • lastname
                                  • passwordhash

                                  save() // speichert diesen user in der datenbank
                                  destroy() // löscht den user aus der datenbank

                                  Da so etwas ja bei jedem Model vorkommt (bei dir User, GuestbookEntry, Permission, Comment, etc.) gibt es so was schon fertig programmiert und heißt ORM Object-Relational Mapping (ein Paar Beispiele wären http://www.propelorm.org/, http://www.doctrine-project.org/, etc.)

                                  (Berechtigungen)    sit schon vorhanden und muss "irgendwie" mitgenutzt werden

                                  Ne klasse Permission mit Methoden wie;

                                  bool userCan($user, $action, $object)
                                  // $action ist z.b. create, update, delete, show
                                  // $object ist z.b. ein anderer User (admin sollte z.b. andere user löschen dürfen) oder GuestbookEntry, Comment, etc.

                                  Detail-Darstellung für Alle, satzweise oder seitenweise
                                  Listendarstellung mit Kurzdaten
                                  Neueintrag
                                  Editierungsmöglichkeit (mit Cookie) innerhalb 24 Stunden
                                  Freigabe/Sperre (Admin)
                                  Kommentar (durch Alle)

                                  Das müsste in 3 Bereiche unterteilt werden die models, den controller und die views (templates).

                                  // Controller
                                  GuestbookEntryController:
                                  void index()
                                  void show($id) {
                                   // nur als Beispiel in Pseudocode
                                   $gb_entry = GuestBookEntryModel::find($id);
                                   $template = new Template("/guestbook_entry/show.phtml");
                                   $template->gb_entry = $gb_entry;
                                   $template->run();
                                  }
                                  void create($array)
                                  void update($id, $array)
                                  void destroy($id)

                                  // Models
                                  GuestbookEntryModel:

                                  • id
                                  • user
                                  • title
                                  • body
                                    void save()
                                    bool validate()
                                    void destroy()
                                    array comments()

                                  CommentModel:

                                  • id
                                  • user
                                  • guestbook_entry_id
                                  • body
                                    void save()
                                    bool validate()
                                    void destroy()

                                  UserModel

                                  • username
                                  • email
                                  • firsname
                                  • lastname
                                  • passwordhash
                                    void save()
                                    bool validate()
                                    void destroy()

                                  // Templates
                                  Können normale .phtml-Dateien sein oder man benutzt so was wie Smarty.

                                  /views/guestbook_entry/show.phtml

                                  <div class="guestbook-entry">
                                   <h2><?php echo $this->gb_entry->getTitle() ?></h2>
                                   <p>User: <?php echo $this->gb_entry->user->getUsername() ?></p>
                                   <p><?php echo $this->gb_entry->getBody() ?></p>
                                  </div>

                                  Es sollen nur die Klassen geladen werden, die für das jeweilige Dokument am Browser erforderlich sind.

                                  Das ist ehrlich gesagt das letzte deiner Probleme, die Implementierung dessen wird vielleicht die Seiten vielleicht 2 Milisekunden früher ausgeben und Autoload löst das Problem ja auch einigermaßen elegant.

                                  Zum Erben, du könntest dir eine klasse Model basteln, die schon sachen wie destroy() implementiert weil die allgemeingültig sind wenn du in jedem Model eine $id hast und davon dann in GuestbookEntryModel, CommentModel und UserModel erben, dann brauchst du das da einzeln nicht mehr implementieren.

                                  Ansonsten, aber das ist für den Anfang vielleicht overkill, man sieht dass GuestbookEntryModel und CommentModel fast gleich aussehen, sie unterscheiden sich eigentlich nur durch title in GuestbookEntryModel, da könnte man sich überlegen ob GuestbookEntryModel nicht von CommentModel erben könnte und nur durch $title erweitert wird.

                                  Jeena

                                  1. Tach!

                                    $user wäre ein Objekt der Klasse User die dann einen solchen User repräsentiert mit sachen wie:
                                    [eigenschaften]
                                    save() // speichert diesen user in der datenbank
                                    destroy() // löscht den user aus der datenbank

                                    Eine Kuh melkt sich nicht selbst. Oder in deinem Kontext: Nicht das Bier braut sich, sondern du tust es.
                                    Ein Nutzer muss nicht wissen, wie er sich zu persistieren hat. Das schafft nur unnötige Abhängigkeiten zwischen dem User und der Datenbank.

                                    Eine übliche Vorgehensweise ist, dass Daten enthaltende Objekte von einem Datenkontext überwacht werden und man dem eine Speicherauftrag gibt, woraufhin er sich um das Auslesen der zu speichernden Eigenschaften seiner Schäfchen kümmer und sie speichert.

                                    dedlfix.

                                    1. Hallo,

                                      Eine Kuh melkt sich nicht selbst. Oder in deinem Kontext: Nicht das Bier braut sich, sondern du tust es.

                                      Oh ja das tue ich, es blubbern seit 3 Wochen im Abstellraum wieder 25 liter vor sich hin.

                                      Eine übliche Vorgehensweise ist, dass Daten enthaltende Objekte von einem Datenkontext überwacht werden und man dem eine Speicherauftrag gibt, woraufhin er sich um das Auslesen der zu speichernden Eigenschaften seiner Schäfchen kümmer und sie speichert.

                                      Ich weiß dass Apples CoreData macht das so allerdings it es kein ORM sondern ein Object Graph Management Framework, die ORMs die ich kenne machen das alle so dass sie die grundlegenden CRUD-Operationen (create, read, update, destroy) direkt am Objekt machen, so weit ich mich erinnere nennt man das Active record pattern und ich finde ihn leicht verständlich und leicht implementierbar, dafür aber nicht so mächtig wie das was du beschreibst.

                                      Ein Nutzer muss nicht wissen, wie er sich zu persistieren hat. Das schafft nur unnötige Abhängigkeiten zwischen dem User und der Datenbank.

                                      Naja zwischen user und datenbank nicht wirklich wenn man es sinnvoll implementiert und die Zugriffe auf die Datenbank der Elternklasse überlässt.

                                      Jeena

                                      1. Tach!

                                        die ORMs die ich kenne machen das alle so dass sie die grundlegenden CRUD-Operationen (create, read, update, destroy) direkt am Objekt machen, so weit ich mich erinnere nennt man das Active record pattern und ich finde ihn leicht verständlich und leicht implementierbar, dafür aber nicht so mächtig wie das was du beschreibst.

                                        Naja, leicht verständlich ist das mit dem Datenkontext auch, zumindest mit dem Entity Framework unter .NET. Schau mal auf [http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-5]. Leider hat die Seite keine Anker zum Direktverlinken. Nach dem ersten Drittel kommt die Überschrift "Looking at the Store Manager Controller code" (unter den Webseiten-Bildern). Nach der Initialisierung des DB-Kontextes (db, Klasse MusicStoreEntities) werden die Action-Methoden des Controllers vorgestellt. Die HTTP-Request-Methode und den Pfad der URL sieht man jeweils als Kommentar darüberstehen. Üblicherweise reicht eine Zeile Code, um sich seine Daten zu holen. Das nächste Interessante kommt bei "POST: /StoreManager/Create". Die Formulardaten werden von der ASP.NET-MVC-Infrastruktur aufbereitet, ein neues Album-Objekt erstellt und der Methode Create als Argument übergeben. Nach der hier nicht zu sehenden (weil ausgelagerten) Validitätsprüfung wird das album zu den anderen im Kontext hinzugefügt und das Kontext-Objekt (db) beauftragt, die Änderungen zu speichern. Viel verständlicher kann man es kaum machen. Edit und Delete sind ebenso einfach zu bekommen.

                                        Dass da jede Menge Zeug im Hintergrund abläuft, ist nur von Vorteil, so kann man sich besser auf seine Geschäftslogik konzentrieren.

                                        Ein Nutzer muss nicht wissen, wie er sich zu persistieren hat. Das schafft nur unnötige Abhängigkeiten zwischen dem User und der Datenbank.
                                        Naja zwischen user und datenbank nicht wirklich wenn man es sinnvoll implementiert und die Zugriffe auf die Datenbank der Elternklasse überlässt.

                                        Auch in der Elternklasse sorgt sowas dafür, dass Eigenschaften und Methoden sichtbar sind, die mit der Verwaltung und nichts mit den Daten selbst zu tun haben. Am besten ist, wenn das Datenobjekt von all dem Kram nichts mitbekommt. Unter C# nennt man das POCO - Plain Old C# Object (in Part 4 des verlinkten Tutorials sieht man, dass die Album-Klasse einfach nur sie selbst ist und nichts erbt.) Auf PHP angewendet kommt da kein seriöser Name zustande ...

                                        dedlfix.

                                        1. Hallo,

                                          Naja, leicht verständlich ist das mit dem Datenkontext auch, zumindest mit dem Entity Framework unter .NET. Schau mal auf [http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-5].

                                          Ich habe es mir mal angeschaut und ja es sieht leicht verständlich aus, bei einer Webanwendung wo sowieso immer nur eines nach dem anderen gemacht wird kommt man auch nicht so mit den Contexten durcheinander wie bei einer normalen Applikation wo dann viele Sachen gleichzeitig ablaufen und man den context sharen möchte um sich nicht allzusehr mit verschiedenen Versionen der Daten in verschiedenen contexten zu verwirren. Ich kenne den Ansatz sonst nur von Apple, und da sieht das alles nicht so aufgeräumt aus.

                                          Auch in der Elternklasse sorgt sowas dafür, dass Eigenschaften und Methoden sichtbar sind, die mit der Verwaltung und nichts mit den Daten selbst zu tun haben. Am besten ist, wenn das Datenobjekt von all dem Kram nichts mitbekommt.

                                          Guter Einwand, da muss ich mir mal Gedanken drüber machen.

                                          Jeena

                                2. Moin!

                                  Machs doch mal an einem echten Webbeispiel fest und nicht an diesen hinkenden Enten miot Zweizylindermotor.

                                  Simples Beispiel: Gästebuch mit Kommentarfunktion und Bildupload-Möglichkeit.

                                  Das ist alles andere als ein simples Beispiel. Insbesondere unbekannt ist ja, in welche Infrastruktur sich dein Produkt einfügen muss.

                                  Allerdings: Angenommen, es existiert schon irgendein Shop, ein Blog und weiterer Content auf der Webseite, und jetzt soll dieses Gästebuch ohne weitere Verzwickung einfach nur dazu kommen, aber die Userdatenbank der Admins mitbenutzen... dann heißt es kapseln. Wie ist die Anmeldeinformation durch das Login gespeichert? In $_SESSION? Wrapper-Klasse drumherumlegen. Für alle weiteren externen Informationen, die relevant eingebunden werden sollen, ebenfalls.

                                  Vom Webdesigner soll nachher nur eine neue Klasse "guestbook" instantiiert und eingebunden werden und die Darstellungsmethode aufgerufen werden. Alles andere muss automatisch ablaufen.

                                  Du meinst:

                                  require_once("Zend/Application.php");  
                                  $application = new Zend_Application;  
                                  $application->bootstrap()->run(); // passende Parameter fehlen hier noch  
                                  
                                  

                                  Noch weniger wird schwierig. Ich glaube auch nicht, dass man ein Gästebuch komplett in EINEN Aufruf packen kann, denn man muss ja mindestens zwei unterschiedliche Requests behandeln: Lesen und Schreiben. Das Admin-Interface braucht auch noch Platz - insbesondere als URL.

                                  Sprich: Du könntest problemlos eine komplette Zend-Applikation liefern, die "einfach nur" eingebunden wird und sich parallel zu den bestehenden URLs in einem eigenen Pfad breitmacht.

                                  Wie teilst Du nun die Klassen auf?

                                  Unabhängig von allem, was schon da ist. Für die Aufgaben, die schon erledigt sind, braucht man anstelle einer eigenen Umsetzung nur den Part, der lesend zugreift, und zwar als Wrapper um die Legacy-Info oder den zugehörigen Legacy-Code. Ggf. wird man um den Code auch herumarbeiten können, indem man direkt eine dahinterstehende Datenquelle (z.B. Datenbanken) über alternative Wege kontaktiert.

                                  Aufgaben:
                                  (Authentifizierung) ist schon vorhanden und muss mitgenutzt werden
                                  (Berechtigungen)    sit schon vorhanden und muss "irgendwie" mitgenutzt werden

                                  Authentifizierung ist also irrelevant, weil das Durchführen derselben dazu führt, dass irgendwo die Berechtigungen des Users abgelegt sind. Insofern ist nur das Berechtigungssystem zu wrappen, ggf. um die neuen Rechte für dein Modul zu erweitern (wo das genau sein sollte, wäre zu analysieren - lässt sich das Legacy-System einfach um weitere Rechte-Kennungen erweitern?).

                                  Detail-Darstellung für Alle, satzweise oder seitenweise
                                  Listendarstellung mit Kurzdaten
                                  Neueintrag
                                  Editierungsmöglichkeit (mit Cookie) innerhalb 24 Stunden
                                  Freigabe/Sperre (Admin)
                                  Kommentar (durch Alle)

                                  Ja und? Die notwendigen Daten sind passend zu modellieren, in einem persistenten Speicher zu halten, und auf Anfrage auszugeben in der jeweils gewünschten Darstellung. Da man es unweigerlich mit mehreren verschiedenen URLs zu tun haben wird, mit denen die Feinheiten der Darstellung gesteuert werden, ist ein existierendes MVC-Framework an dieser Stelle hilfreich, damit man sich direkt um produktiven Code kümmern kann, und nicht zunächst zum hundertsten Male Klassen zur Formularvalidierung schreiben muss.

                                  Vorgabe:
                                  Es sollen nur die Klassen geladen werden, die für das jeweilige Dokument am Browser erforderlich sind.

                                  Irrelevant, aber mit Autoloading automatisch erfüllt.

                                  - Sven Rautenberg

                              2. مرحبا

                                Ein Ford wird wohl von Auto die meisten Attribute erben, aber eine Ente wohl kaum.

                                Meinst du mit Attributen Eigenschaften? Ich kenne für die nutzung von Klassen nur Konstanten, Eigenschaften und Methoden. Was sind Attribute?

                                mfg

                                1. Hallo,

                                  Meinst du mit Attributen Eigenschaften? Ich kenne für die nutzung von Klassen nur Konstanten, Eigenschaften und Methoden. Was sind Attribute?

                                  Naja die Egenschaften sind die Attribute, ist nur ein anderes Wort dafür. Ein Auto hätte das Attribut numberOfwheels, die Ente das Attribut numberOfLegs zum Beispiel.

                                  Jeena

                                  1. مرحبا

                                    Naja die Egenschaften sind die Attribute, ist nur ein anderes Wort dafür.

                                    Danke für den Link.

                                    Ein Auto hätte das Attribut numberOfwheels, die Ente das Attribut numberOfLegs zum Beispiel.

                                    Von der Theorie her leuchtet mir das alles ein, aber ich kriege es nicht für mich in die Praxis umgesetzt. Kommt Zeit kommt OOP.

                                    mfg

                            2. مرحبا

                              "Autoload" war da schon das richtige Stichwort. Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.

                              Genau daran scheitere ich auch. Die meisten Funktionen, die ich schreibe, sind so klein, dass ich garnicht weiss, wie ich das in eine Klasse kriegen soll.

                              mfg

                              1. Moin!

                                "Autoload" war da schon das richtige Stichwort. Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.

                                Genau daran scheitere ich auch. Die meisten Funktionen, die ich schreibe, sind so klein, dass ich garnicht weiss, wie ich das in eine Klasse kriegen soll.

                                Ganz simpel: Eine Klasse drumherum schreiben und die Funktion statisch machen und statisch aufrufen - das erlaubt zumindest schon mal das Autoloading, denn das geht nur mit Klassen.

                                Alles andere erfordert aber Erfahrung mit OOP - und OOP lebt davon, dass man nur kleine Funktionen hat, die Magie steckt darin, dass diese kleinen Funktionen zu komplexen Konstrukten zusammengesetzt werden.

                                - Sven Rautenberg

                                1. مرحبا

                                  Ganz simpel: Eine Klasse drumherum schreiben und die Funktion statisch machen und statisch aufrufen - das erlaubt zumindest schon mal das Autoloading, denn das geht nur mit Klassen.

                                  Daran hatte ich auch schon gedacht, als ich meine Pagination-Klasse fertig hatte. Allerdings befürchtete ich, PHP4-Programmierer zu werden, wenn ich anfgange, alles Blindlings in Klassen zu Kapseln, trotzdem aber Prozedural programmiere.

                                  Ich werde es mal mit der Klasse drumrum Variante probieren.

                                  mfg

                                  1. Moin!

                                    Ganz simpel: Eine Klasse drumherum schreiben und die Funktion statisch machen und statisch aufrufen - das erlaubt zumindest schon mal das Autoloading, denn das geht nur mit Klassen.

                                    Daran hatte ich auch schon gedacht, als ich meine Pagination-Klasse fertig hatte. Allerdings befürchtete ich, PHP4-Programmierer zu werden, wenn ich anfgange, alles Blindlings in Klassen zu Kapseln, trotzdem aber Prozedural programmiere.

                                    Irgendwie muss man ja anfangen. Und da OOP ein reichhaltiges Angebot an Möglichkeiten, aber auch an Andersheit hat, ist es im Prinzip auch egal, an welchem Ende man beginnt, sich damit zu beschäftigen.

                                    Solange du vom Singleton-Pattern die Finger lässt... :)

                                    - Sven Rautenberg

                                    1. مرحبا

                                      Solange du vom Singleton-Pattern die Finger lässt... :)

                                      Ich werde mich stets bemühen ;)

                                      Nur mal eine Grundsatzfrage zu OOP, die ich bislang nicht ganz verstanden habe.

                                      Ich möchte mein Menu-Script in eine Klasse einschliessen.

                                      Das wäre soweit kein Problem. Allerdings benutze ich das Script in erweiterter Form. Ich erstelle damit unterschiedliche Menus, also Main-Navi, Footer-Navi u.s.w.
                                      Muss ich jetzt, wenn ich das Menu in eine Klasse packe und die unterschiedlichen Menus anfordere, jeweils ein Neues Objekt erzeugen, da alles Unterschiedliche "Menus" sind. Oder lade ich das Menu in ein Objekt und rufe die verschiedenen Menus mittels Methoden und entsprechenden Parametern auf? Was ist hier der bessere Stil?

                                      Also

                                      $menu = new Navigation;  
                                      $menu->main_navi;  
                                      $menu->footer_navi;
                                      

                                      Oder

                                      $menu = new Navigation('main_navi');  
                                      $menu->build_menu;  
                                      $footermenu = new Navigation('footer_navi');  
                                      $footermenu->build_menu;
                                      

                                      Was wäre besser? Mir wäre die erste Variante Lieber, aber darum geht's ja nicht.

                                      mfg

                                      --
                                      Die neuen 4 Jahreszeiten: Frühling, ARSCHLOCH, Herbst und Winter!
                                      1. Moin!

                                        Nur mal eine Grundsatzfrage zu OOP, die ich bislang nicht ganz verstanden habe.

                                        Ich möchte mein Menu-Script in eine Klasse einschliessen.

                                        Das wäre soweit kein Problem. Allerdings benutze ich das Script in erweiterter Form. Ich erstelle damit unterschiedliche Menus, also Main-Navi, Footer-Navi u.s.w.

                                        Gehe zunächst nicht davon aus, was du im Inneren der Klasse alles tun musst. Gehe vom Äußeren aus: Wie willst du deine Klasse verwenden? Was ist "einfach" in der Anwendung?

                                        OOP zu benutzen zerfällt im Prinzip in zwei Phasen:

                                        Erstens: Initialisieren und konfigurieren für die zweite Phase.
                                        Zweitens: Benutzen für den gewünschten Zweck.

                                        Bei der Betrachtung des Nutzungszwecks und der Einfachheit in der Anwendung sollte man sich nicht von den möglicherweise komplizierten Konstruktionen der ersten Phase beeinflussen lassen - dafür findet man ebenfalls eine Lösung.

                                        Deshalb die Frage: Was willst du eigentlich "genau" tun? Welche tatsächlichen Anwendungsfälle willst du haben? Es reicht, mit dem ersten anzufangen. :)

                                        Muss ich jetzt, wenn ich das Menu in eine Klasse packe und die unterschiedlichen Menus anfordere, jeweils ein Neues Objekt erzeugen, da alles Unterschiedliche "Menus" sind. Oder lade ich das Menu in ein Objekt und rufe die verschiedenen Menus mittels Methoden und entsprechenden Parametern auf? Was ist hier der bessere Stil?

                                        Hängt davon ab, wie du es benutzen willst.

                                        Was ist dein Anwendungsfall? Offenbar: "Gib mir ein Menü aus!" Halt, nicht ganz: "Gib mir zwei unterschiedliche Menüs aus!"

                                        Was ist der einfachste Code, den du dir vorstellen kannst, um die Ausgabe _eines_ Menüs zu starten?

                                        Sowas:

                                        $navi = new Navi();  
                                        $navi->writeMenu();  
                                        
                                        

                                        Was wäre die einfachste Implementierung, die in dieser Funktion drinsteckt? Vermutlich sowas:

                                        echo "Menü....";  
                                        
                                        

                                        Ja, absolut banal, aber als Resultat hast du eine Klasse, einen Methodenaufruf und ein ausgegebenes Menü! Das erfüllt den ersten Case "Gib mir ein Menü aus!" absolut.

                                        Jetzt kommt allerdings der Test-Nazi in mir durch und sagt: Ein "echo" in der Funktion kann man schlecht testen, die Menüfunktion sollte deshalb lieber einen String des Menüs zurückgeben, anstatt ihn direkt auszugeben. Das "echo" zum Browser sollte man lieber "außerhalb" regeln. Also bauen wir die Klasse mal kurz um: "echo" fliegt raus, wird durch "return" ersetzt, und die Methode heißt "getHtmlMenu()".

                                        Nächstes Problem: "Zwei unterschiedliche Menüs"...

                                        "Gleich" ist kein Problem, einfach die Klasse zweimal instantiieren und an zwei Stellen benutzen. Die eine Klasse an zwei Stellen benutzen wäre nicht dasselbe, weil das ja tatsächlich zweimal dasselbe Menü ausgibt - zwei Instanzen einer Klasse könnten sich ja aber innerlich unterscheiden.

                                        Da ist also direkt die Frage: Worin unterscheiden sie sich? Inhaltlich, oder auch von der erzeugten HTML-Struktur? Beginnen wir mit "inhaltlich": Das Menü kriegt irgendwoher eine Liste von anzuzeigenden Linktexten. Wie kann man in PHP am einfachsten eine Liste mit Linktexten übergeben? Als Array!

                                        Also tun wir das auch:

                                        $links = array('Home', 'Impressum');  
                                        $navi = new Navi($links);  
                                        $navi->getHtmlMenu();
                                        

                                        Wie sieht jetzt eigentlich der Code für die Klasse genauer aus?

                                        class Navi  
                                        {  
                                          private $_links;  
                                          
                                          public function __construct(array $links)  
                                          {  
                                            $this->_links = $links;  
                                          }  
                                          
                                          public function getHtmlMenu()  
                                          {  
                                            $menustring = "";  
                                            foreach ($this->_links as $link) {  
                                              $menustring .= '<a href="">' . htmlspecialchars($link) . '</a>';  
                                            }  
                                            return $menustring;  
                                          }  
                                        }  
                                        
                                        

                                        In diese Klasse eine andere Liste von Links hineinzutun ist jetzt erstmal nicht sonderlich schwer. :)

                                        Aber das andere Problem ist noch ungelöst: Wie kriegt man unterschiedlich aussehende, wohlmöglich unterschiedlich strukturierte Links hin? Das Grundelement für eine dynamische Linkliste ist eigentlich immer eine Kombination aus Linkziel und Linktext, aber wie dekoriert man da einen hübsch aussehenden HTML-Text drumherum? Mit dem Decorator-Pattern!

                                        Das Decorator-Pattern nimmt ein eher "nacktes" Objekt und kapselt es ein, damit es nach außen besser aussieht oder auch eine Aufgabe angepasster ausführen kann.

                                        Der hart kodierte HTML-Link-String dort oben ist also nicht sehr dynamisch, sollte aber genau das sein. Bauen wir also einen ganz simple Decorator, der erstmal genau dasselbe tut:

                                        class Navi_Decorator  
                                        {  
                                          public function getHtmlLink($link)  
                                          {  
                                            return '<a href="">' . htmlspecialchars($link) . '</a>';  
                                          }  
                                        }  
                                        
                                        

                                        Wie kommt der Decorator ins Menü? Instantiieren und übergeben.

                                        class Navi  
                                        {  
                                          private $_links;  
                                          
                                          private $_decorator;  
                                          
                                          public function __construct(array $links, Navi_Decorator $decorator)  
                                          {  
                                            $this->_links = $links;  
                                            $this->_decorator = $decorator;  
                                          }  
                                          
                                          public function getHtmlMenu()  
                                          {  
                                            $menustring = "";  
                                            foreach ($this->_links as $link) {  
                                              $menustring .= $this->_decorator->getHtmlLink($link);  
                                            }  
                                            return $menustring;  
                                          }  
                                        }  
                                        
                                        

                                        Aktuelle komplette Anwendung:

                                        $links = array('Home', 'Impressum');  
                                        $decorator = new Navi_Decorator();  
                                        $navi = new Navi($links, $decorator);  
                                        $navi->getHtmlMenu();
                                        

                                        Du siehst: Obwohl die Funktionalität sich außen kaum verändert, werden durch die inneren Komponenten, die man von außen mit hineintut, eventuell gewaltige Dinge bewegt. Deshalb noch als letztes die Sache mit den diversen Eigenschaften, die ein Link außer seinem Text hat: Linkziel und ggf. target.

                                        Diese Dreierkombination passt eigentlich ziemlich gut ebenfalls wieder in eine Klasse:

                                        class Navi_Link  
                                        {  
                                          public $label = "";  
                                          public $url = "";  
                                          public $target = "";  
                                        }  
                                        
                                        

                                        Die jetzt anstehende Änderung wäre also, anstelle eines simplen Strings in einem Array lieber ein Array von diesen Klassen in die Navigation hinein zu tun. Wie sich Label und URL in den Linktext hineinpflanzen, dürfte leicht zu erkennen sein.

                                        Wie man den HTML-Text des Links verändert, ist auch recht leicht: Man erbt von der existierenden Basis-Decorator-Klasse (aufgrund des Type-Hints im Konstruktor von "Navi" notwendig) und modifiziert den auszugebenden String.

                                        An dieser Stelle existiert also ein Klassenkonstrukt aus zunächst drei Basisklassen, die sich schon um alle wesentlichen Aspekte eines Menüs kümmern können: Die Erzeugung des Gesamtmenüs steckt in "Navi", die Datenhaltung für einen einzelnen Link ist in "Navi_Link", und das Erscheinungsbild in "Navi_Decorator".

                                        Weitere Schritte, die jetzt denkbar wären:
                                        1. Wie kriegt man tatsächlich aus einer Datenbank eine Liste von Links erzeugt? Sowas ist jetzt eigentlich recht einfach, man kann z.B. mysqli_fetch_object("Navi_Link") aufrufen und erzeugt sich so ein passendes Link-Objekt, wenn man zuvor einen Query mit "label", "url" und "target" abgeschickt hat.

                                        2. Eine Liste von Links in ein Array zu packen, wenn man doch den Datenbankzugriff ebenfalls objektorientiert erledigen will, ist vielleicht keine so wahnsinnig gute Idee. Bei kleinen Ergebnissen ist das Kopieren in ein Array egal, bei großen Ergebnissen kostet es recht viel Speicher zusätzlich - denn PHP hält das DB-Ergebnis ja schon seinerseits in einem Puffer bereit. Außerdem bietet PHP die Möglichkeit, dass Objekte ebenfalls mit "foreach" durchlaufen werden können, indem man das Iterator- oder das IteratorAggregate-Interface implementiert. Man würde also kein Array mehr in "Navi" hineingeben, sondern ein iterierbares Objekt "Navi_Linklist", welches dann einzelne "Navi_Link"-Objekte ausspuckt.

                                        3. Das Dekorieren eines einzelnen Links ist schön und gut, aber damit allein kriegt man noch keine vollständige Linkliste hin - es fehlt ein Dekorator der gesamten Liste. Die einzelnen Links könnten so z.B. in "<li>" eingepackt werden, die gesamte Liste dann in ein "<ul>".

                                        4. Die Dekoratoren immer mit festen Zeichenketten zu versehen ist irgendwann auch nicht mehr schön, man möchte sie vermutlich von außen konfigurieren können: Welches HTML-Element wird benutzt? Gibts Attribute? CSS-Klassen? Der in allen Dekoratoren gemeinsam genutzte Code wandert in die gemeinsame Elternklasse - Vererbung!

                                        ...
                                        Mir würde vermutlich noch viel mehr einfallen, aber ich höre an dieser Stelle erst einmal auf.

                                        - Sven Rautenberg

                                        1. مرحبا

                                          Erstmal ein dickes Danke Schön für dieses Beispiel und die Erklärung. Ich habe min. 100.000 mal auf fachlich hilfreich geklickt, wurde leider nicht gezählt :)

                                          Ich hätte jetzt schon die ein oder andere frage zu den Klassen, aber dafür ist es wohl zu spät. Das Danke musste aber jetzt noch raus.

                                          mfg

                                          --
                                          Die neuen 4 Jahreszeiten: Frühling, ARSCHLOCH, Herbst und Winter!
                                        2. Tach!

                                          Darf ich dir ans Herz legen, daraus einen Artikel für das Wiki zu erzeugen?

                                          dedlfix.

                                        3. Hello Sven,

                                          vielen Dank für diesen ausführlichen Beitrag. Ich pflichte Dedlfix' bitte bei :-)

                                          Bis vor den "Decorator" konnte ich Dir folgen. Nur, wie dieser auszusehen hat, habe ich noch nicht durchschaut.

                                          Aktuelle komplette Anwendung:

                                          $links = array('Home', 'Impressum');

                                          $decorator = new Navi_Decorator();
                                          $navi = new Navi($links, $decorator);
                                          $navi->getHtmlMenu();

                                          
                                          >   
                                          > Du siehst: Obwohl die Funktionalität sich außen kaum verändert, werden durch die inneren Komponenten, die man von außen mit hineintut, eventuell gewaltige Dinge bewegt. Deshalb noch als letztes die Sache mit den diversen Eigenschaften, die ein Link außer seinem Text hat: Linkziel und ggf. target.  
                                          >   
                                          > Diese Dreierkombination passt eigentlich ziemlich gut ebenfalls wieder in eine Klasse:  
                                          >   
                                          > ~~~php
                                          
                                          class Navi_Link  
                                          
                                          > {  
                                          >   public $label = "";  
                                          >   public $url = "";  
                                          >   public $target = "";  
                                          > }  
                                          > 
                                          
                                          

                                          Ich habe bisher immer reine Datenklassen gebaut und dann in einer "Bind-Class" die Daten mit den Templates verbunden. Das könnten ja auch andere, als HTML sein.

                                          Die Templates bestehen bei mir aus mehrern Teilen: Statischer Teil "Frame" und dynamische Teile "Repeat-Areas".

                                          Ein fertiges Datenobjekt besteht daher auch aus dem statischen Teil und ggf. mehreren Repeat-Areas.

                                          So ist es auch möglich, dass Templates aus dem Userspace geladen werden, während Funktionalität und Daten nur aus dem "Supervisor-Space" kommen.

                                          Wie ich das "ich übergebe Datenblöcke als Arrays" aber nun in saubere OOP verwandele, wo echte Objekte übergeben werden müssen und auch nur die passenden übergeben werden können, ohne dabei die Universalität der Bind-Klasse zu verlieren, da hänge ich jetzt.

                                          Liebe Grüße aus dem schönen Oberharz

                                          Tom vom Berg

                                          --
                                           ☻_
                                          /▌
                                          / \ Nur selber lernen macht schlau
                                          http://bergpost.annerschbarrich.de
                                        4. مرحبا

                                          Grundsätzlich habe ich das meiste soweit verstanden. Doch einiges wirft mehr fragen auf, als es beantwortet :)

                                          Die Parameter im Konstruktor der Navi-Klasse

                                          class Navi  
                                          {  
                                            #...  
                                            public function __construct(array $links, Navi_Decorator $decorator)  
                                            {  
                                              # ...  
                                            }  
                                          }
                                          

                                          Das meiste in deinem Bsp. ist selbstredend, nur der 2. Parameter. Warum steht da die Klasse Navi_Decorator drin, diese wird doch später als neues Objekt Seperat aufgerufen? Und wie nennt sich die Schreibweise für diese Parameter, also das neben der Variable "array" steht, oder der Klassenname (hier "Navi_Decorator")?

                                          $links = array('Home', 'Impressum');  
                                          $decorator = new Navi_Decorator();  # Hier wird das Objekt doch bereits instantiiert? Ist das richtig formuliert?  
                                          $navi = new Navi($links, $decorator);  
                                          $navi->getHtmlMenu();
                                          

                                          Nächste Frage wäre:

                                          class Navi_Link  
                                          {  
                                            public $label = "";  
                                            public $url = "";  
                                            public $target = "";  
                                          }
                                          

                                          Die jetzt anstehende Änderung wäre also, anstelle eines simplen Strings in einem Array lieber ein Array von diesen Klassen in die Navigation hinein zu tun.

                                          Wie darf ich ein "Array von diesen Klassen" verstehen? Soll ich jeden einzelnen Link des Menus in einem Objejt speichern?
                                          Wäre das nicht zuviel Overhead, wenn viele Links exisitieren?

                                          1. Eine Liste von Links in ein Array zu packen, wenn man doch den Datenbankzugriff ebenfalls objektorientiert erledigen will, ist vielleicht keine so wahnsinnig gute Idee.

                                          Ich habe es bei mir bislang so gelöst, dass ich das Resultat aus der Datenbank direkt als Linkliste speichere, ohne erst in ein Array zu kopieren. Das ist ja noch mit das einfachste.

                                          Außerdem bietet PHP die Möglichkeit, dass Objekte ebenfalls mit "foreach" durchlaufen werden können, indem man das Iterator- oder das IteratorAggregate-Interface implementiert. Man würde also kein Array mehr in "Navi" hineingeben, sondern ein iterierbares Objekt "Navi_Linklist", welches dann einzelne "Navi_Link"-Objekte ausspuckt.

                                          Hier stehe ich komplett auf den Schlauch.

                                          mfg

                                          --
                                          Die neuen 4 Jahreszeiten: Frühling, ARSCHLOCH, Herbst und Winter!
                                          1. Hallo,

                                            Das meiste in deinem Bsp. ist selbstredend, nur der 2. Parameter. Warum steht da die Klasse Navi_Decorator drin, diese wird doch später als neues Objekt Seperat aufgerufen? Und wie nennt sich die Schreibweise für diese Parameter, also das neben der Variable "array" steht, oder der Klassenname (hier "Navi_Decorator")?

                                            Das sind Type Hints http://php.net/manual/en/language.oop5.typehinting.php und nur dazu da dass man weiß welchen Typ diese Argumente sein sollten.

                                            Wie darf ich ein "Array von diesen Klassen" verstehen? Soll ich jeden einzelnen Link des Menus in einem Objejt speichern?

                                            Ja ganz genau.

                                            Wäre das nicht zuviel Overhead, wenn viele Links exisitieren?

                                            Nein, darüber brauchst du dir keine Gedanken machen, schon gar nicht bei PHP.

                                            Außerdem bietet PHP die Möglichkeit, dass Objekte ebenfalls mit "foreach" durchlaufen werden können, indem man das Iterator- oder das IteratorAggregate-Interface implementiert. Man würde also kein Array mehr in "Navi" hineingeben, sondern ein iterierbares Objekt "Navi_Linklist", welches dann einzelne "Navi_Link"-Objekte ausspuckt.
                                            Hier stehe ich komplett auf den Schlauch.

                                            PHPs foreach funktioniert nicht nur mit array sondern mit jedem Objekt das das IteratorAggregate-Interface implementiert (array tut das auch).

                                            Ich spekuliere jetzt über die exakte implementation aber das sollte es vielleicht etwas klarer machen, foreach könnte man auch so implementieren (die methode next() ist hier beschrieben http://www.php.net/manual/en/class.iterator.php):

                                            $my_array = array(1,2,3,4);  
                                            $iterator = $my_array.getIterator();  
                                            while($item = $iterator.next()) {  
                                             echo $item;  
                                            }
                                            

                                            wäre das gleiche wie:

                                            $my_array = array(1,2,3,4);  
                                            foreach($my_array as $item) {  
                                             echo $item;  
                                            }
                                            

                                            Das gibt es übrigens in so ziemlich allen anderen Sprachen z.B. Java

                                            Jeena

                                            1. Tach!

                                              Das sind Type Hints http://php.net/manual/en/language.oop5.typehinting.php und nur dazu da dass man weiß welchen Typ diese Argumente sein sollten.

                                              Ja, aber PHP prüft auch darauf. Ansonsten wäre der Type Hint funktionslos und es hätte auch ein Kommentar getan.

                                              PHPs foreach funktioniert nicht nur mit array sondern mit jedem Objekt das das IteratorAggregate-Interface implementiert (array tut das auch).

                                              Nein zum Klammernzusatz, array ist kein Objekt. Das konnte man schon iterieren, lange bevor es die SPL-Erweiterung und damit Iterator(Aggregate) gab. Die SPL hat nur die Iterierbarkeit dem OOP-Teil PHPs hinzugefügt.

                                              var_dump(array() instanceof Traversable); ergibt false.

                                              Wenn PHP-Arrays Iterator(Aggregate) implementiert hätten, müsste das true ergeben.

                                              $my_array = array(1,2,3,4);

                                              $iterator = $my_array.getIterator();
                                              while($item = $iterator.next()) {
                                              echo $item;
                                              }

                                              
                                              >   
                                              > wäre das gleiche wie:  
                                              >   
                                              > ~~~php
                                              
                                              $my_array = array(1,2,3,4);  
                                              
                                              > foreach($my_array as $item) {  
                                              >  echo $item;  
                                              > }
                                              
                                              

                                              Theoretisch ja, praktisch geht's nicht.

                                              dedlfix.

                                            2. Moin!

                                              PHPs foreach funktioniert nicht nur mit array sondern mit jedem Objekt das das IteratorAggregate-Interface implementiert (array tut das auch).

                                              Nein, echte Arrays implementieren kein Interface, weil sie kein Objekt sind. PHP erkennt intern am Typ, ob es nativ iterieren kann, oder ob ein Objekt ein iterierbares Interface anbietet.

                                              Ich spekuliere jetzt über die exakte implementation aber das sollte es vielleicht etwas klarer machen, foreach könnte man auch so implementieren (die methode next() ist hier beschrieben http://www.php.net/manual/en/class.iterator.php):

                                              $my_array = array(1,2,3,4);

                                              $iterator = $my_array.getIterator();
                                              while($item = $iterator.next()) {
                                              echo $item;
                                              }

                                                
                                              Das ist KEIN PHP-Code. Er ist falsch. Also davon nicht verwirren lassen.  
                                                
                                              
                                              > wäre das gleiche wie:  
                                              >   
                                              > ~~~php
                                              
                                              $my_array = array(1,2,3,4);  
                                              
                                              > foreach($my_array as $item) {  
                                              >  echo $item;  
                                              > }
                                              
                                              

                                              Es gibt keinen Grund, foreach NICHT zu verwenden. foreach ist die performanteste Art, in PHP zu iterieren.

                                              - Sven Rautenberg

                                              1. Hallo,

                                                Nein, echte Arrays implementieren kein Interface, weil sie kein Objekt sind. PHP erkennt intern am Typ, ob es nativ iterieren kann, oder ob ein Objekt ein iterierbares Interface anbietet.

                                                Oh waia, ist ja alles noch viel schlimmer als ich erwartet habe. Sorry für die Falschaussagen, ich hätte besser recherchieren sollen.

                                                Jeena

                                          2. Tach!

                                            public function __construct(array $links, Navi_Decorator $decorator)
                                            Das meiste in deinem Bsp. ist selbstredend, nur der 2. Parameter. Warum steht da die Klasse Navi_Decorator drin, diese wird doch später als neues Objekt Seperat aufgerufen? Und wie nennt sich die Schreibweise für diese Parameter, also das neben der Variable "array" steht, oder der Klassenname (hier "Navi_Decorator")?

                                            Type Hinting. Das hilft zu verhindern, dass du Objekte einer falschen Klasse übergeben kannst. Arrays sind ins Type Hinting auch aufgenommen, andere Typen gehen nicht.

                                            Wie darf ich ein "Array von diesen Klassen" verstehen? Soll ich jeden einzelnen Link des Menus in einem Objejt speichern?

                                            Ja.

                                            Wäre das nicht zuviel Overhead, wenn viele Links exisitieren?

                                            Jein. Ein Objekt mit drei Eigenschaften ist nicht wesentlich anders strukturiert als ein Array mit drei Elementen. Deine Überlegung ist auch eher theoretischer Natur. Monstermenüs wirst du kaum bauen. Aber wenn du die Betrachtung mal auf Wald-und-Wiesen-Script vs. OOP ausweitest - einen Tod musst du sterben. Die Eleganz der OOP mit ihrem Overhead oder zu Fuß und Performance. Performance ist in der Regel ausreichend vorhanden. Die Eleganz einer Lösung wird sich bei Erweiterungen positiv bemerkbar machen.

                                            Ich habe es bei mir bislang so gelöst, dass ich das Resultat aus der Datenbank direkt als Linkliste speichere, ohne erst in ein Array zu kopieren. Das ist ja noch mit das einfachste.

                                            Ja, aber unflexibel. Du baust dann das gesamte Konstrukt um, statt nebenher einen neuen Dekorator zu erstellen und diesen dann einfach nur zu übergeben. Und wenn das nicht gefällt, nimmst du ebenso einfach wieder den alten Dekorator. Bei der direkten Methode musst du den bisherigen Code wiederherstellen.

                                            Außerdem bietet PHP die Möglichkeit, dass Objekte ebenfalls mit "foreach" durchlaufen werden können, indem man das Iterator- oder das IteratorAggregate-Interface implementiert. Man würde also kein Array mehr in "Navi" hineingeben, sondern ein iterierbares Objekt "Navi_Linklist", welches dann einzelne "Navi_Link"-Objekte ausspuckt.
                                            Hier stehe ich komplett auf den Schlauch.

                                            Ein iterierbares Objekt empfiehlt sich dann, wenn neben dem reinen Speichern, das auch ein Array erledigen kann, noch weitere Eigenschaften und Methoden benötigt werden. Hier ist es aber anders. Wenn du Datenbeschaffung und die weitere Verarbeitung trennen willst, musst du zuerst die Daten in einer geeigneten Umgebung ablegen (ein Array zum Beispiel). Der Verarbeiter kann dann mit ihnen machen was er will. Das Anlegen kostet zumindest Zeit, Platz weniger, PHP ist intelligent genug, keine Kopien im Speicher anzulegen, wenn sich die eigentlich aus deiner Sicht kopierten Daten nicht ändern. Den Aspekt des Fetchens und Array-Anlegens kannst du vereinfachen, indem du aus der Datenbeschaffung kein Array sondern nur einen Iterator an den Verarbeiter weitergibst. Der Verarbeiter iteriert diesen Iterator und dieser fetcht bei jedem Schritt einen Datensatz und übergibt ihn. Für dich sieht es aus wie foreach über ein Array, in echt ist es aber ein Lazy Fetching. Der Nachteil an der Lösung ist jedoch, wenn du beim Verarbeiten mehrfach über die Daten iterieren musst. Der Iterator ist nach einem Durchlauf abgelaufen. Ein Neustarten scheitert normalerweise daran, dass das Fetchen ein einmaliger Vorgang ist. Die MySQL(i)-API legt jedoch im Hintergrund die gesamte Ergebnisdatenmenge aus der SQL-Abfrage im Speicher ab, so dass du den Fetch-Zeiger problemlos wieder auf den Anfang stellen kannst, wenn ein erneuter Fetchvorgang stattfinden soll. Iterator ist für den einmaligen Gebrauch vorgesehen, mit IteratorAggregate kann man mehrfach durchlaufbare Iteratoren erstellen.

                                            dedlfix.

                                            1. Moin!

                                              Die MySQL(i)-API legt jedoch im Hintergrund die gesamte Ergebnisdatenmenge aus der SQL-Abfrage im Speicher ab, so dass du den Fetch-Zeiger problemlos wieder auf den Anfang stellen kannst, wenn ein erneuter Fetchvorgang stattfinden soll. Iterator ist für den einmaligen Gebrauch vorgesehen, mit IteratorAggregate kann man mehrfach durchlaufbare Iteratoren erstellen.

                                              Dem letzten Satz widerspreche ich, denn es gibt keinen Unterschied in der Anzahl möglicher Durchläufe zwischen den Interfaces "Iterator" und "IteratorAggregate".

                                              Außer natürlich, man implementiert es fehlerhaft.

                                              - Sven Rautenberg

                                              1. Tach!

                                                Die MySQL(i)-API legt jedoch im Hintergrund die gesamte Ergebnisdatenmenge aus der SQL-Abfrage im Speicher ab, so dass du den Fetch-Zeiger problemlos wieder auf den Anfang stellen kannst, wenn ein erneuter Fetchvorgang stattfinden soll. Iterator ist für den einmaligen Gebrauch vorgesehen, mit IteratorAggregate kann man mehrfach durchlaufbare Iteratoren erstellen.
                                                Dem letzten Satz widerspreche ich, denn es gibt keinen Unterschied in der Anzahl möglicher Durchläufe zwischen den Interfaces "Iterator" und "IteratorAggregate".

                                                Ok, ist schon lange her, als ich das letzte Mal damit in Berührung kam. Das Interface Iterator hat eine Methode rewind(), mit der man zurückstellen kann. Dann war der Unterschied zwischen beiden Interfaces der, dass man mit Iterator keinen zweiten Durchlauf zur gleichen Zeit starten kann, weil seine Methoden direkt im Datenobjekt integriert sind und keinen Hinweis zum Aufrufer bekommen, um zwischen mehreren zu unterscheiden. IteratorAggregate hingegen liefert für jeden Verwender einen eigenständigen Iterator, der sich separat merken kann, wie weit er ist.

                                                dedlfix.

                                          3. Moin!

                                            Grundsätzlich habe ich das meiste soweit verstanden. Doch einiges wirft mehr fragen auf, als es beantwortet :)

                                            Ausgezeichnet!

                                            Die Parameter im Konstruktor der Navi-Klasse

                                            class Navi

                                            {
                                              #...
                                              public function __construct(array $links, Navi_Decorator $decorator)
                                              {
                                                # ...
                                              }
                                            }

                                            
                                            >   
                                            > Das meiste in deinem Bsp. ist selbstredend, nur der 2. Parameter. Warum steht da die Klasse Navi\_Decorator drin, diese wird doch später als neues Objekt Seperat aufgerufen? Und wie nennt sich die Schreibweise für diese Parameter, also das neben der Variable "array" steht, oder der Klassenname (hier "Navi\_Decorator")?  
                                              
                                            Das ist ein sogenannter Type-Hint (bei beiden Parametern). Er bewirkt, dass PHP sich beschwert, wenn der übergebene Wert einen anderen Typ hat, als im Hint drinsteht.  
                                              
                                            Für den Decorator-Parameter bedeutet dass: Die Prüfung `$decorator instanceof Navi_Decorator`{:.language-php} muss true ergeben. Das ist der Fall, wenn $decorator entweder ein Objekt der Klasse Navi\_Decorator ist, oder von einer Klasse erbt, die Navi\_Decorator ist, oder ein Interface implementiert, welches Navi\_Decorator heißt, oder von einer Klasse erbt, welche ein Interface Navi\_Decorator implementiert.  
                                              
                                            So ein Type-Hint erzwingt, dass der übergebene Parameter ein Objekt ist, in dem gewisse Methoden existieren - denn ein Interface definiert, welche Methoden ein Objekt zu implementieren hat, und eine Vererbung von einer Klasse garantiert ebenfalls, dass die Methoden der Klasse vorhanden sind und aufgerufen werden können.  
                                              
                                            Da die Klasse Navi\_Decorator in meinem Beispiel genau eine Methode namens "getHtmlLink()" implementiert, kann ich durch den Typehint garantieren, dass das übergebene Objekt eines ist, welches diese Methode hat. Ich muss beim späteren Verwenden dieses dann intern abgespeicherten Objektes nicht mehr prüfen, ob es diese Methode gibt, und es kann auch nicht passieren, dass irgendein unwissender anderer Programmierer mir ein Objekt rein reicht, bei dem dieser Aufruf scheitert, weil seine Klasse ganz andere Methoden kennt.  
                                              
                                              
                                            
                                            > Nächste Frage wäre:  
                                            >   
                                            > ~~~php
                                            
                                            class Navi_Link  
                                            
                                            > {  
                                            >   public $label = "";  
                                            >   public $url = "";  
                                            >   public $target = "";  
                                            > }
                                            
                                            

                                            Die jetzt anstehende Änderung wäre also, anstelle eines simplen Strings in einem Array lieber ein Array von diesen Klassen in die Navigation hinein zu tun.

                                            Wie darf ich ein "Array von diesen Klassen" verstehen? Soll ich jeden einzelnen Link des Menus in einem Objejt speichern?
                                            Wäre das nicht zuviel Overhead, wenn viele Links exisitieren?

                                            Angenommen, diese drei oben genannten Informationen gehörten zu deinem Link dazu. Dann hast du die Wahl, entweder ein Objekt zu nehmen, oder ein Array, um diese drei Werte zu gruppieren. Ein Objekt ist dabei im Prinzip genauso groß, wie ein Array, denn der Code der zugehörigen Klasse wird sowieso nur einmal im Speicher gehalten, entscheidend sind lediglich die Objektvariablen. Und die sind in beiden Fällen identisch, nämlich drei Strings.

                                            Ich will nicht abstreiten, dass es eventuell doch Unterschiede in der Speichergröße geben könnte. Aber diese sind zu vernachlässigen. Du wirst mit zehn Links kein Problem haben - und wenn du ein Problem kriegst, dann ist es relativ egal, ob du bei 10.000 Links oder bei 23.615 Links ein Problem kriegst - die zu ergreifende Lösung wäre in jedem Fall, sich die Link-Information dann nicht auf einen Schlag komplett in den Speicher zu legen, sondern im Bedarfsfall zu holen. Dann wiederum bist du allerdings mit Objekten als Datenspeicher deutlich besser dran, denn nur damit könntest du sowas einfach realisieren. Ein komplettes Array hingegen kann nur plump im Speicher liegen - entweder es passt rein, oder nicht.

                                            Das "Array von Klassen" ist sowas:

                                            $linkHome = new Navi_Link();  
                                            $linkHome->label = "Home";  
                                              
                                            $links = array($linkHome);  
                                            
                                            

                                            Es ist an dieser Stelle aber nicht sehr schön, dass Objekte in einem Array lagern - es ist kompliziert, die dort manuell reinzukriegen, und erzeugt irgendwann vielleicht die gerade erwähnten Platzprobleme.

                                            1. Eine Liste von Links in ein Array zu packen, wenn man doch den Datenbankzugriff ebenfalls objektorientiert erledigen will, ist vielleicht keine so wahnsinnig gute Idee.

                                            Ich habe es bei mir bislang so gelöst, dass ich das Resultat aus der Datenbank direkt als Linkliste speichere, ohne erst in ein Array zu kopieren. Das ist ja noch mit das einfachste.

                                            Ja, das ist die prozedurale Herangehensweise: Datenbankabfrage und Erzeugung des HTML-Ausgabestrings ist wild durchmischt - am besten steht alles noch insgesamt in irgendeinem Template drin.

                                            In OOP trennt man die Dinge inhaltlich voneinander. Datenbankabfrage hat mit HTML-Erzeugung nichts zu tun, lediglich die aus der DB abgefragten Daten werden irgendwie zur Ausgabe transferiert.

                                            Am Ende geschieht, wenn man die Objektorientierung gut gemacht hat, im Code entrollt ziemlich genau dasselbe: Ein Query wird losgeschickt, ein DB-Ergebnis entsteht, es wird einzeln abgefragt und in HTML-Links verwandelt. Der Unterschied ist lediglich, dass während dieser Ausführung deutlich mehr Funktionsaufrufe passieren, die innerhalb der beteiligten Klassen hin- und herwechseln, um jeweils eine notwendige Teilaufgabe zu erledigen. Der Programmierer hingegen sieht im Code diese Wechsel nicht so deutlich, sondern konzentriert sich innerhalb einer Klasse auf einen möglichst simplen Teilaspekt.

                                            Auf diese Weise kann man durch OOP komplexe Probleme mit relativ einfachem Code lösen, und sehr komplexe Probleme überhaupt erst bewältigen. Die Erzeugung eines Menüs ist kein komplexes Problem, sondern eigentlich noch relativ einfach, weshalb diese komplexitätsreduzierende Wirkung von OOP eigentlich eher lächerlich wirkt - deshalb nicht abschrecken lassen. :)

                                            Außerdem bietet PHP die Möglichkeit, dass Objekte ebenfalls mit "foreach" durchlaufen werden können, indem man das Iterator- oder das IteratorAggregate-Interface implementiert. Man würde also kein Array mehr in "Navi" hineingeben, sondern ein iterierbares Objekt "Navi_Linklist", welches dann einzelne "Navi_Link"-Objekte ausspuckt.

                                            Hier stehe ich komplett auf den Schlauch.

                                            Es gibt das Interface "Iterator". Siehe http://de3.php.net/manual/de/language.oop5.iterations.php, Beispiel 2.

                                            Dieses Interface verlangt vom Programmierer das Schreiben von fünf Methoden in seiner Klasse, die in einer definierten Reihenfolge aufgerufen werden, wenn man ein Objekt dieser Klasse mit "foreach" durchlaufen will.

                                            Ausgangsvoraussetzung: Du willst ein Objekt haben, dass in irgendeiner Weise eine "Liste" von Werten ist (was auch immer diese Werte sind). Für das Menü-Problem brauchst du eine Liste von Links, und die Links selbst sind Objekte vom Typ "Navi_Link".

                                            Du möchtest folgenden Code haben:

                                            $linkliste = $db->getQueryErgebnisMitLinkliste(); // Was auch immer das tut... irgendwas mit Datenbank  
                                              
                                            foreach ($linkliste as $link)  
                                            {  
                                              // hier was mit dem einzelnen Link machen  
                                            }  
                                            
                                            

                                            Wenn in $linkliste ein Objekt vom Typ "Navi_Linkliste" enthalten ist, muss dieses Objekt:
                                            1. intern eine Liste der Links verwalten
                                            2. nach außen die fünf Methoden des Interfaces implementieren, um
                                            2a. mit der Methode "rewind()" die Liste auf den Anfang zu spulen,
                                            2b. mit der Methode "valid()" anzuzeigen, dass der "Listenzeiger" auf einem gültigen Element steht,
                                            2c. mit der Methode "current()" genau dieses Element zurückzugeben,
                                            2d. mit der Methode "key()" den Schlüssel dieses Elements zurückzugeben (das ist in einem Array der Index)
                                            2e. mit der Methode "next()" den Listenzeiger auf das nächste Element setzen.

                                            Diese Listenoperationen sind ganz nah an den für Arrays verfügbaren Funktionen "reset()", "current()" und "next()" sowie "each()".

                                            Man muss aber seine Liste intern nicht in einem Array abspeichern. Beispielsweise kann man stattdessen intern ein Result-Objekt von mysqli abspeichern. Dieses Objekt repräsentiert das Ergebnis eines Querys, und es bietet nette Funktionen an, mit denen man das Ergebnis schrittweise auslesen kann:

                                            rewind() ruft data_seek(0) auf, um zurückzuspulen, und liest den nächsten Datensatz mit fetch_object() aus. Der Erfolg wird als Flag gespeichert, genauso wie dieser Datensatz.
                                            valid() liest das Erfolgsflag aus.
                                            current() gibt das gerade ausgelesene Ergebnis zurück.
                                            key() könnte den primary Key aus dem Ergebnis kennen und ausgeben, oder es wird intern die Anzahl der Datensätze mitgezählt und als Key ausgegeben.
                                            next() liest den nächsten Datensatz mit fetch_object() aus und aktualisiert das Erfolgsflag.

                                            - Sven Rautenberg

                                            1. Tach!

                                              Das ist ein sogenannter Type-Hint (bei beiden Parametern). Er bewirkt, dass PHP sich beschwert, wenn der übergebene Wert einen anderen Typ hat, als im Hint drinsteht.

                                              Ausnahme ist null, das passt überall durch.

                                              Da die Klasse Navi_Decorator in meinem Beispiel genau eine Methode namens "getHtmlLink()" implementiert, kann ich durch den Typehint garantieren, dass das übergebene Objekt eines ist, welches diese Methode hat. Ich muss beim späteren Verwenden dieses dann intern abgespeicherten Objektes nicht mehr prüfen, ob es diese Methode gibt, und es kann auch nicht passieren, dass irgendein unwissender anderer Programmierer mir ein Objekt rein reicht, bei dem dieser Aufruf scheitert, weil seine Klasse ganz andere Methoden kennt.

                                              Lediglich auf null muss man noch per Hand prüfen, das sinnvollerweise gleich bei der Übergabe. Sonst kracht es später auch, weil null keine Mitglieder hat, auf die man zugreifen könnte.

                                              dedlfix.

                                              1. Moin!

                                                Tach!

                                                Das ist ein sogenannter Type-Hint (bei beiden Parametern). Er bewirkt, dass PHP sich beschwert, wenn der übergebene Wert einen anderen Typ hat, als im Hint drinsteht.

                                                Ausnahme ist null, das passt überall durch.

                                                Nein, nicht in PHP.

                                                Catchable fatal error: Argument 1 passed to Foo::__construct() must be an instance of Bar, null given

                                                Da die Klasse Navi_Decorator in meinem Beispiel genau eine Methode namens "getHtmlLink()" implementiert, kann ich durch den Typehint garantieren, dass das übergebene Objekt eines ist, welches diese Methode hat. Ich muss beim späteren Verwenden dieses dann intern abgespeicherten Objektes nicht mehr prüfen, ob es diese Methode gibt, und es kann auch nicht passieren, dass irgendein unwissender anderer Programmierer mir ein Objekt rein reicht, bei dem dieser Aufruf scheitert, weil seine Klasse ganz andere Methoden kennt.

                                                Lediglich auf null muss man noch per Hand prüfen, das sinnvollerweise gleich bei der Übergabe. Sonst kracht es später auch, weil null keine Mitglieder hat, auf die man zugreifen könnte.

                                                Eben nicht.

                                                "null" geht nur, wenn man es als Default-Wert in der Deklaration angegeben hat, um den Parameter optional zu machen.

                                                - Sven Rautenberg

                                                1. Tach!

                                                  Ausnahme ist null, das passt überall durch.
                                                  Nein, nicht in PHP.
                                                  "null" geht nur, wenn man es als Default-Wert in der Deklaration angegeben hat, um den Parameter optional zu machen.

                                                  Hmm, Mist, wieder daneben. PHP ist eben kein C#.

                                                  dedlfix.

                                              2. Hello,

                                                ich hänge mich hier nochmal mit einer anderen Frage rein. Bevor ich die OOP-Regeln von PHP dann mit denen anderer Sprachen durcheinanderbringe, frage ich lieber nochmal.

                                                Welchen Gültigkeitsbereich und welche SIchtbarheiten haben Klassen und ihre Instanzen in PHP?

                                                  
                                                  
                                                class A  
                                                {  
                                                  ## Definitionen  
                                                }  
                                                  
                                                class B  
                                                {  
                                                  ## Definitionen  
                                                  $__A = new A;  
                                                }  
                                                  
                                                $__B = new B;  
                                                  
                                                
                                                

                                                ist das möglich? Ist A in B sichtbar und damit benutzbar?
                                                Wann geht die Instanz $__A kaputt (wird beseitigt)?
                                                Wie rufe ich die Methoden von A mit Hilfe der Instanz $__A in $__B auf?

                                                Wie ist das bei geschachtelten Klassen, also wenn z.B. class C innerhalb von B deklariert und definiert wird?

                                                Liebe Grüße aus dem schönen Oberharz

                                                Tom vom Berg

                                                --
                                                 ☻_
                                                /▌
                                                / \ Nur selber lernen macht schlau
                                                http://bergpost.annerschbarrich.de
                                                1. Tach!

                                                  Welchen Gültigkeitsbereich und welche SIchtbarheiten haben Klassen und ihre Instanzen in PHP?

                                                  Es gibt keinen Unterschied zu Funktionen und Variablen anderen Typs.

                                                  class B {

                                                  ## Definitionen
                                                    $__A = new A;
                                                  }

                                                  
                                                  > ist das möglich? Ist A in B sichtbar und damit benutzbar?  
                                                    
                                                  Nein, ohne eine Methode drumrum ist das ein Syntaxfehler. Da fehlt noch ein Zugriffsmodifizierer (public (oder var), protected oder private). Anschließend ist die Eigenschaft über $this->\_\_A in allen Methoden verfügbar.  
                                                    
                                                  
                                                  > Wann geht die Instanz $\_\_A kaputt (wird beseitigt)?  
                                                    
                                                  Wenn sie nicht mehr referenziert wird. $this->\_\_A ist nur eine mögliche Referenz auf das Objekt. Vielleicht hast du ja noch mehr darauf im Laufe des Scripts erstellt. Die Frage an sich ist aber relativ uninteressant. Sobald du keine Referenz mehr auf das Objekt hältst, hast du sowieso keinen Einfluss mehr darauf und an seinem Zustand (noch da oder schon weggeräumt) kannst du nichts mehr ändern. Eher interessant wäre, wann der Destruktor aufgerufen wird. Aber das ist im PHP-Handbuch beantwortet.  
                                                    
                                                  
                                                  > Wie rufe ich die Methoden von A mit Hilfe der Instanz $\_\_A in $\_\_B auf?  
                                                    
                                                  OOP-Gundlagenwissen. Du hältst entweder in B eine Eigenschaft, die A referenziert ($this->\_\_A) oder eine lokale Variable in einer Methode ($foo). Anschließend folgt -> und der Name der Eigenschaft (ohne $) oder Methode.  
                                                    
                                                  
                                                  > Wie ist das bei geschachtelten Klassen, also wenn z.B. class C innerhalb von B deklariert und definiert wird?  
                                                    
                                                  Gibt es nicht.  
                                                    
                                                    
                                                  dedlfix.
                                                  
                                            2. مرحبا

                                              Sorry, dass ich mich erst wieder so spät einklinke, hatte einen kleinen Crash am Wochenende und war ein bisschen weggetreten.

                                              Das ist ein sogenannter Type-Hint

                                              Ok, Type-Hint habe ich soweit verstanden.

                                              Das mit dem Menu war aber glaube ich keine Gute Idee. Sieht zwar leicht aus, aber die Objektiteration macht mich jetzt schon ganz Bibber und Bange.
                                              Können wir vielleicht auf etwas leichteres umsteigen? Eine leichte Übung wäre Angepasste Inhalte für mobile Endgeräte (hoffe ich zumindest :)).
                                              Ich habe damit mal rumgespielt (das mit dem Menu ist mir derzeit zu Hoch), und habe das Bsp. vom Dirk in eine Klasse gepackt.

                                              Die Klasse soll ermitteln, ob der anfordernde Client ein Mobil-Gerät ist, oder nicht (das funktioniert bereits).
                                              Darüberhinaus muss die Klasse auch mit Cookies und GET-Variablen arbeiten. Sollte ich diese Variablen von Aussen mittels Parametern der Klasse übergeben, oder soll ich hier innerhalb der Klasse direkt auf die Globalen Variablen zugreifen?

                                              Meine Idee für die Klasse wäre so:

                                              class CheckMobile  
                                              {  
                                                public $_device;  // @return "Mobile" oder "Desktop"  
                                                // Sind die Useragents hier richtig aufgehoben?  
                                                private $_agents = array('up.browser','up.link','mmp','symbian','smartphone'  
                                                                        ,'midp','wap','phone','Windows CE','Pocket','mobile'  
                                                                        ,'-mobile','portable','SDA','PDA','handheld','iemobile'  
                                                                        ,'palmos','minimo','avantgo','cHTML','blackberry'  
                                                                        ,'opera mini','fennec','nokia','googlebot-mobile');  
                                                
                                                public function __construct()  
                                                {  
                                                  $this->_device = $this->GetDevice();  
                                                }  
                                                
                                                private function isMobile() // Prüfen der Browserkennung  
                                                {  
                                                  for ($i = 0; $i < count($this->_agents); $i++)  
                                                    if (isset($_SERVER["HTTP_USER_AGENT"]) AND strpos(strtolower($_SERVER['HTTP_USER_AGENT']), $this->_agents[$i]) !== false)  
                                                      return true;  
                                                  return false;  
                                                }  
                                                
                                                private function GetDevice()  
                                                {  
                                                  if ($this->isMobile())  
                                                    return 'Mobile';  
                                                  else  
                                                    return 'Desktop';  
                                                }  
                                                
                                              }  
                                                
                                              // Aufruf  
                                              $_device = new CheckMobile();  
                                              echo $_device->_device;
                                              

                                              Ist der Ansatz soweit Ok? Als nächstes müsste ich Cookies und GET-Parameter prüfen, wie sollte ich da drangehen? Die Variablen von Aussen der Klasse hinzufügen und innerhalb der Klasse wie "normale" Variablen behandeln, oder direkt die Superglobalen Variablen verwenden? Also $_COOKIES und $_GET?

                                              Sorry das ich gleich wieder das Thema wechsele, aber bevor ich wieder nur die hälfte verstehe.
                                              Und die Fertige Klasse könnte später ins Wiki. 2 Fliegen mit einer Klatsche :)

                                              mfg

                                              --
                                              Die neuen 4 Jahreszeiten: Frühling, ARSCHLOCH, Herbst und Winter!
                                              1. Moin!

                                                Das mit dem Menu war aber glaube ich keine Gute Idee. Sieht zwar leicht aus, aber die Objektiteration macht mich jetzt schon ganz Bibber und Bange.

                                                Es war die beste Idee, die sich angeboten hat. :)

                                                Können wir vielleicht auf etwas leichteres umsteigen? Eine leichte Übung wäre Angepasste Inhalte für mobile Endgeräte (hoffe ich zumindest :)).

                                                OOP wird nicht leichter, wenn man was anderes macht, denn es geht immer und jederzeit um die gleichen Prinzipien: Das Trennen von Zuständigkeiten in eigenständige Objekte, die ihrerseits für andere Zuständigkeiten weitere Objekte benutzen, dies nach außen aber nicht zeigen.

                                                Insofern sorgt OOP, wenn sie gut gemacht ist, für eine Reduktion der gefühlten Komplexität an jeder Stelle des Codes. Die Gesamtkomplexität ist zwar weiterhin hoch, allerdings ist an keiner Stelle eine besorgniserregende Konzentration von Komplexität zu sehen, denn die wäre schädlich.

                                                Es gibt für die Erstellung von OOP-Lösungen ein schönes Wort: "S.O.L.I.D."

                                                S - Single Responsibility: Eine Klasse sollte genau einen Zweck haben, nicht mehr als einen. Wenn in der Antwort auf die Frage "Was tut die Klasse?" ein "und" vorkommt, dann tut die Klasse vermutlich mehr als eine Sache - schlecht!

                                                O - Open-closed Prinziple: Eine Klasse sollte offen für Erweiterungen, aber geschlossen gegenüber Veränderungen sein. Das geht vor allem in die Problematik, ob man eine Klasse nochmal extenden sollte, oder sie lieber unverändert benutzt, und die Variation außenherum programmiert.

                                                L - Liskov Substitution Prinziple: Das ist wohl die komplexeste Forderung der fünf. Eine Klasse A kann andere Klassen (z.B. B) zum Arbeiten benutzen sollen. Nach dem Liskov-Ersetzungs-Prinzip soll man sowohl die Klasse A erweitern können zu A', oder auch die Klasse B zu B', ohne dass dadurch irgendwelche Methoden der Grundklassen inkompatibel geändert werden.

                                                I - Interface Segregation Principle: Spezielle kleinere Interfaces für eine konkrete Aufgabe sind besser, als ein großes, allgemeines Interface.

                                                D - Dependency Inversion Principle: Dahinter verbirgt sich u.A. das, was als Dependency Injection bekannt ist. Wenn eine Klasse eine weitere Klasse für Unteraufgaben benötigt, soll es diese Klasse nicht selbst instanziieren, sondern sich von außen hereinreichen lassen.

                                                Ich habe damit mal rumgespielt (das mit dem Menu ist mir derzeit zu Hoch), und habe das Bsp. vom Dirk in eine Klasse gepackt.

                                                Dirk hat nur eine Funktion in seinem Artikel gehabt. Den in ein OOP-Beispiel umzusetzen ist nicht sehr leicht. Oder schwer - dann aber nicht wirklich OOP. :)

                                                Die Klasse soll ermitteln, ob der anfordernde Client ein Mobil-Gerät ist, oder nicht (das funktioniert bereits).

                                                Wenn man da jetzt OOP einbringen will, würde ich folgende Aspekte mal hinterfragen:

                                                1. Woher kommt die Liste der erkennbaren User-Agents, die "mobil" sind? Da die Liste nicht auf kosmisch konstanten Fakten basiert, gibt es keinen Grund, sie statisch im Code einzubauen, sondern man würde sie leichter veränderbar in einer Konfigurationsdatei halten. Daraus folgt: Deine Klasse benötigt ein Konfigurationsobjekt, mit dem die Liste der User-Agent-Werte ausgelesen werden kann, und diese Klasse kann wiederum Unterobjekte benutzen, um sich z.B. an INI-Dateien, XML-Dateien oder Datenbanken anzudocken - oder auch ganz normale PHP-Arrays in Include-Dateien auslesen.

                                                2. Der zweite Punkt ist das Auswerten des aktuellen HTTP-Requests. Hier direkt auf die superglobalen Variablen zuzugreifen ist zwar möglich, aber nicht elegant oder OOP. Eigentlich sollte man hier lieber ein Request-Objekt haben, in dem die aktuellen Zustände des laufenden HTTP-Requests vom Browser gekapselt sind. Dieses Objekt wird in einer PHP-Applikation an einer zentralen Stelle mit den Inhalten der superglobalen Variablen gefüllt und von dort aus an die Stellen weitergetragen, die sich im Sinne der Applikation für den Request interessieren sollen.

                                                3. Ich bin auch kritisch eingestellt gegenüber der Strategie, schon sehr viel im Konstruktor auszuführen. Da Objekte üblicherweise erst einmal erstellt werden, und eventuell nie benutzt werden, zumindest nicht in allen Aspekten, sollte man die Dinge, die nicht zwingend zur Lebensfähigkeit des Objekts gehören, möglichst erst dann ausführen, wenn es tatsächlich notwendig ist.

                                                4. Am Ende noch ein wenig Codekritik zu den Namen der Methoden: Wir haben "isMobile" und "GetDevice" - einmal klein und einmal groß.

                                                5. Und der Namen der public Property "_device" ist auch nicht so glücklich - mit Unterstrichen hat man zu Zeiten von PHP 4, als alle Properties public waren, die gekennzeichnet, die man als protected oder private ansehen wollte und deshalb nicht von außen benutzen sollte.

                                                Darüberhinaus muss die Klasse auch mit Cookies und GET-Variablen arbeiten. Sollte ich diese Variablen von Aussen mittels Parametern der Klasse übergeben, oder soll ich hier innerhalb der Klasse direkt auf die Globalen Variablen zugreifen?

                                                Meine Idee für die Klasse wäre so:

                                                class CheckMobile

                                                {
                                                  public $_device;  // @return "Mobile" oder "Desktop"
                                                  // Sind die Useragents hier richtig aufgehoben?
                                                  private $_agents = array('up.browser','up.link','mmp','symbian','smartphone'
                                                                          ,'midp','wap','phone','Windows CE','Pocket','mobile'
                                                                          ,'-mobile','portable','SDA','PDA','handheld','iemobile'
                                                                          ,'palmos','minimo','avantgo','cHTML','blackberry'
                                                                          ,'opera mini','fennec','nokia','googlebot-mobile');

                                                public function __construct()
                                                  {
                                                    $this->_device = $this->GetDevice();
                                                  }

                                                private function isMobile() // Prüfen der Browserkennung
                                                  {
                                                    for ($i = 0; $i < count($this->_agents); $i++)
                                                      if (isset($_SERVER["HTTP_USER_AGENT"]) AND strpos(strtolower($_SERVER['HTTP_USER_AGENT']), $this->_agents[$i]) !== false)
                                                        return true;
                                                    return false;
                                                  }

                                                private function GetDevice()
                                                  {
                                                    if ($this->isMobile())
                                                      return 'Mobile';
                                                    else
                                                      return 'Desktop';
                                                  }

                                                }

                                                // Aufruf
                                                $_device = new CheckMobile();
                                                echo $_device->_device;

                                                  
                                                  
                                                 - Sven Rautenberg
                                                
                                                1. Hallo Sven!

                                                  Ich habe die Diskussion hier mitgelesen, da ich auch noch einiges in Sachen OOP zu lernen habe. Vielen Dank daher für alle Tipps und Anregungen hier.
                                                  Nun stellt sich mir aber eine Frage:

                                                  D - Dependency Inversion Principle: Dahinter verbirgt sich u.A. das, was als Dependency Injection bekannt ist. Wenn eine Klasse eine weitere Klasse für Unteraufgaben benötigt, soll es diese Klasse nicht selbst instanziieren, sondern sich von außen hereinreichen lassen.

                                                  Ich dachte, es wäre ebenfalls ein Grundsatz von OOP, dass eine Klasse oder allgemein ein Benutzer einer Klasse möglichst wenig über deren „Inneres“ wissen müssen sollte. Wie verträgt sich dies mit dem Grundsatz, in der Klasse benötigte Instanzen per DI mitzugeben, statt in der Klasse selbst zu erzeugen? Sollte ich nicht optimalerweise gar nicht wissen müssen, wie Unteraufgaben innerhalb der Klasse gelöst werden?

                                                  Und kommt es nicht auch zu einer Kollision damit,

                                                  1. Ich bin auch kritisch eingestellt gegenüber der Strategie, schon sehr viel im Konstruktor auszuführen. Da Objekte üblicherweise erst einmal erstellt werden, und eventuell nie benutzt werden, zumindest nicht in allen Aspekten, sollte man die Dinge, die nicht zwingend zur Lebensfähigkeit des Objekts gehören, möglichst erst dann ausführen, wenn es tatsächlich notwendig ist.

                                                  denn wenn die Instanz einer für Unteraufgaben benötigten Klasse von außen herein gereicht wird, wird sie ja ebenfalls in jedem Fall erzeugt – auch wenn sie vielleicht nie benötigt wird.

                                                  Ich will damit deinen Empfehlungen keinesfalls widersprechen, sondern sie nur richtig verstehen.

                                                  Vielen Dank und schönen Abend noch,

                                                  Claudius

                                                  1. Hallo zusammen!

                                                    Bevor der Thread im Archiv verschwindet, würde ich gerne nochmal auf ihn aufmerksam machen. In meinem Posting habe ich zwar Sven angesprochen, aber ich würde mich natürlich auch über Antworten anderer freuen …

                                                    Viele Grüße,

                                                    Claudius

                                                  2. Tach!

                                                    D - Dependency Inversion Principle: Dahinter verbirgt sich u.A. das, was als Dependency Injection bekannt ist. Wenn eine Klasse eine weitere Klasse für Unteraufgaben benötigt, soll es diese Klasse nicht selbst instanziieren, sondern sich von außen hereinreichen lassen.
                                                    Ich dachte, es wäre ebenfalls ein Grundsatz von OOP, dass eine Klasse oder allgemein ein Benutzer einer Klasse möglichst wenig über deren „Inneres“ wissen müssen sollte. Wie verträgt sich dies mit dem Grundsatz, in der Klasse benötigte Instanzen per DI mitzugeben, statt in der Klasse selbst zu erzeugen? Sollte ich nicht optimalerweise gar nicht wissen müssen, wie Unteraufgaben innerhalb der Klasse gelöst werden?

                                                    Eine Klasse ist ja kein Selbstzweck. Sie muss irgendwas bearbeiten und braucht dazu Daten oder andere Objekte. Wenn du kein DI-Framework mit automatischer Abhängigkeitsauflösung einsetzen möchtest, kannst du auch Konstruktoren verwenden, die ohne explizite Übergabe sich selbst ein Objekt einer benötigten Klasse erzeugen. Das ist für überschaubare Projekte durchaus ein brauchbarer Kompromiss. Damit lässt du dir immer noch die Möglichkeit offen, ein Mock-Objekt zum Testen oder im Einzelfall irgendwas anderes zu übergeben.

                                                    Bei einem Controller, der für die Bearbeitung von Personendatensätzen ausgelegt ist, ist es außer zu Testzwecken eher ausgeschlossen, dass er mit anderen Datenquellen als dem Personenrepository arbeiten muss. Es ist deshalb wohl keine Unart, dass er sich per Default ein Personenrepository besorgt und für Testläufe auch mit einen Mock-Repository versorgt werden kann. Aber je generischer die Aufgabe einer Klasse ist, desto nützlicher ist es sicherlich, dass sie flexibel zu beeinflussen ist.

                                                    Solche Regeln haben keinen Gesetzescharakter. Sie haben sich gebildet, weil sie sich in der Praxis bei einer Vielzahl von Projekten als günstig erwiesen haben. Das heißt nicht, dass das nun die einzig mögliche OOP-Programmierweise ist. Wenn deine Ansprüche für ein konkretes Projekt weniger hoch sind, darfst du durchaus davon abweichen.

                                                    Und kommt es nicht auch zu einer Kollision damit,

                                                    1. Ich bin auch kritisch eingestellt gegenüber der Strategie, schon sehr viel im Konstruktor auszuführen. Da Objekte üblicherweise erst einmal erstellt werden, und eventuell nie benutzt werden, zumindest nicht in allen Aspekten, sollte man die Dinge, die nicht zwingend zur Lebensfähigkeit des Objekts gehören, möglichst erst dann ausführen, wenn es tatsächlich notwendig ist.
                                                      denn wenn die Instanz einer für Unteraufgaben benötigten Klasse von außen herein gereicht wird, wird sie ja ebenfalls in jedem Fall erzeugt – auch wenn sie vielleicht nie benötigt wird.

                                                    Dependency Injection muss nicht nur über den Konstruktor erfolgen, man kann die notwendigen Dinge auch bei Methodenaufrufen übergeben. Dann muss man sie erst erzeugen, wenn die Methode tatsächlich verwendet wird.

                                                    dedlfix.

                                                    1. Hallo dedlfix!

                                                      Vielen Dank für deine Antwort.

                                                      Solche Regeln haben keinen Gesetzescharakter.

                                                      Ja, vor allem das ist mir gerade klar geworden. Ich war der Meinung, es gäbe nur einen „richtigen“ Weg und je mehr man davon abweicht, desto „falscher“ wird’s. Aber sicher, eine Lösung muss vor allem angemessen und vernünftig sein, dann erst kommen Regeln (oder besser: Richtlinien).

                                                      Dependency Injection muss nicht nur über den Konstruktor erfolgen, man kann die notwendigen Dinge auch bei Methodenaufrufen übergeben. Dann muss man sie erst erzeugen, wenn die Methode tatsächlich verwendet wird.

                                                      Das war mir so bisher auch noch nicht bewusst. Danke dafür!

                                                      Schönes Wochenende,

                                                      Claudius

                                                      1. Moin!

                                                        Dependency Injection muss nicht nur über den Konstruktor erfolgen, man kann die notwendigen Dinge auch bei Methodenaufrufen übergeben. Dann muss man sie erst erzeugen, wenn die Methode tatsächlich verwendet wird.

                                                        Das war mir so bisher auch noch nicht bewusst. Danke dafür!

                                                        In der Tat gibt es eigentlich nur genau ZWEI Methoden, in PHP Dependency Injection zu realisieren:

                                                        1. Übergabe der externen Abhängigkeit als Konstruktorparameter (Konstruktor-Injection).
                                                        2. Übergabe der externen Abhängigkeit über eine Setter-Methode (Setter-Injection).

                                                        Als Variante von 2. gibt es auch noch
                                                        2a. Übergabe der externen Abhängigkeit über eine Setter-Methode, die deshalb existiert, weil die Klasse ein Interface implementiert, welches für DI genutzt wird (Interface-Injection).

                                                        Variante 2a ist vor allem für Frameworks interessant, die die zusammenzuhängenden Klassen untersuchen und die Abhängigkeiten automatisch auflösen können. Es verringert den Aufwand bei der Konfiguration der Klassenzusammensetzung und macht die Abhängigkeiten expliziter, indem es sie in den ausgeführten Code verlagert. Für die manuelle Anwendung ist Interface-Injection allerdings eher nicht notwendig.

                                                        Noch ein wichtiger Punkt: Dependency Injection ist ein Programmierprinzip. Es anzuwenden bedeutet, dass man seine Klassen so schreibt, dass man in diese die innen benutzten Klassen von außen hineintun kann - egal wie das passiert.

                                                        Davon zu unterscheiden sind Dependency Injection Container oder -Frameworks. Diese vereinfachen bzw. automatisieren lediglich das Zusammenbauen eines funktionsfähigen Klassenbaums. DI-Container benötigen zwingend Dependency Injection als Programmierprinzip, aber Dependency Injection benötigt KEINE DI-Container-Frameworks.

                                                        - Sven Rautenberg

                                                  3. Moin!

                                                    D - Dependency Inversion Principle: Dahinter verbirgt sich u.A. das, was als Dependency Injection bekannt ist. Wenn eine Klasse eine weitere Klasse für Unteraufgaben benötigt, soll es diese Klasse nicht selbst instanziieren, sondern sich von außen hereinreichen lassen.

                                                    Ich dachte, es wäre ebenfalls ein Grundsatz von OOP, dass eine Klasse oder allgemein ein Benutzer einer Klasse möglichst wenig über deren „Inneres“ wissen müssen sollte. Wie verträgt sich dies mit dem Grundsatz, in der Klasse benötigte Instanzen per DI mitzugeben, statt in der Klasse selbst zu erzeugen? Sollte ich nicht optimalerweise gar nicht wissen müssen, wie Unteraufgaben innerhalb der Klasse gelöst werden?

                                                    Dass ein Benutzer nicht wissen soll, wie eine Aufgabe konkret erledigt wird, nennt man Abstraktion. Er muss lediglich die Voraussetzungen schaffen und dann die Aufgabe ausführen lassen.

                                                    Nun ist aber keine Abstraktion der realen Welt vollkommen hermetisch.

                                                    Beispielsweise ist HTTP eine Abstraktion zum Zugriff auf Webseiten. Man kann in PHP problemlos file_get_contents("http://de.selfhtml.org/") ausführen und dadurch so tun, als wäre die HTML-Seite eine schlichte Textdatei. Die Abstraktion erfordert lediglich, dass man den Namen kennt (hier also die Adresse), und mit dieser Voraussetzung dann die Aktion ausführt ("Datei lesen").

                                                    Isoliert für sich betrachtet ist an der Abstraktion nichts böses, aber man sollte sich an dieser Stelle eben doch verdeutlichen, was technisch tatsächlich passiert: Es wird eben gerade NICHT auf eine lokale Datei auf der Festplatte des Webservers zugegriffen, sondern über ein potentiell unzuverlässiges Netzwerk mit möglicherweise hohen Latenzen und niedrigen Bandbreiten auf einen entfernten Server.

                                                    Wenn man also sein Programm so schreibt, als würde man davon ausgehen, dass dieser Lesezugriff immer so funktioniert, wie mit einer lokalen Datei, dann wird man sich in den Situationen, in denen das Netzwerk mal unzuverlässig funktioniert, gewisse Probleme einhandeln, weil man nicht darauf geachtet hat, dass hier eine nicht garantiert vorhandene Ressource genutzt wird.

                                                    Auf der anderen Seite verschenkt man unter Umständen auch Optimierungspotential. Denn während es beim Zugriff auf lokale Dateien auf einer einzigen Festplatte nicht wirklich optimal ist, viele Dateien parallel zum Lesen zu öffnen, ist ein paralleler Zugriff auf entfernte Netzressourcen sogar sehr gut geeignet, um das Lesen zu beschleunigen, weil viel Zeit mit Warten durch die Netz-Latenzen verschenkt wird.

                                                    Abstraktion ist also gut, weil es die unnötigen Details einer Aufgabenerledigung versteckt. Aber Abstraktion sollte nie als Blackbox verstanden werden, in die man nicht hineingucken darf. Im Gegenteil, man sollte sich für die Interna durchaus interessieren, um zu verstehen, welche Nutzungsarten eventuell besser sind, als andere.

                                                    Beispielsweise ist PHP als Sprache eine Abstraktion und Blackbox. Man benutzt PHP, ohne sich um die innere Funktionsweise Gedanken machen zu müssen. Aber wenn man weiß, wie PHP intern funktioniert, kann man es eventuell besser anwenden.

                                                    Dasselbe gilt für Datenbanken. Man kann Datenbanken benutzen, ohne wissen zu müssen, wie z.B. die Daten intern abgespeichert werden und wie das Schreiben und Lesen funktioniert. Aber wenn man sich dafür interessiert, wird man beispielsweise feststellen, dass bei MySQL die MyISAM-Tabellentypen viel langsamer beim parallelen Lesen und Schreiben sind, als die InnoDB-Tabellentypen. Rein auf SQL-Ebene ändert sich aber absolut nichts.

                                                    Und kommt es nicht auch zu einer Kollision damit,

                                                    1. Ich bin auch kritisch eingestellt gegenüber der Strategie, schon sehr viel im Konstruktor auszuführen. Da Objekte üblicherweise erst einmal erstellt werden, und eventuell nie benutzt werden, zumindest nicht in allen Aspekten, sollte man die Dinge, die nicht zwingend zur Lebensfähigkeit des Objekts gehören, möglichst erst dann ausführen, wenn es tatsächlich notwendig ist.

                                                    denn wenn die Instanz einer für Unteraufgaben benötigten Klasse von außen herein gereicht wird, wird sie ja ebenfalls in jedem Fall erzeugt – auch wenn sie vielleicht nie benötigt wird.

                                                    Als Beispiel will ich eine Datenbanktabelle auslesen, programmiere mir dafür ein Objekt als Repräsentation der Tabelle, und dieses Objekt benötigt einen Datenbankconnector, um die Abfragen durchführen zu können.

                                                    Mein Tabellenobjekt benötigt zwingend einen Datenbankconnector, ansonsten kann es nicht funktionieren. Es gibt auch keine Möglichkeit, mit einem Default-Datenbankconnector zu arbeiten, denn welche Zugangsdaten soll man dafür nehmen? Also ist das ein Parameter im Konstruktor meines Tabellenobjekts, und damit kommuniziere ich dem Benutzer: Wenn du ein Tabellenobjekt haben willst, musst du einen Datenbankconnector haben.

                                                    Dieser Datenbankconnector muss also instanziiert werden. Und an DIESER Stelle ist es jetzt ein Unterschied, ob das Instanziieren einfach nur die im Konstruktor übergebenen DB-Zugangsdaten wegspeichert, oder ob auch noch gleich die Verbindung zur Datenbank hergestellt, die Zeichencodierung gesetzt und eine intern geführte DB-Statistik aktualisiert wird.

                                                    Ebenso ist es ein Unterschied, ob das Tabellenobjekt den Datenbankconnector seinerseits einfach nur intern wegspeichert, oder ob schon im Konstruktor die ersten Datenbankabfragen gestartet werden. Eventuell kann das Objekt ja das Datenbankschema auslesen und sich aufgrund dieser Informationen intern vorkonfigurieren - was auf den ersten Blick vielleicht nicht schlecht klingt, andererseits aber bedeutet, dass beim Bootstrapping der Applikation für jedes Tabellenobjekt, das instanziiert wird, dann mindestens ein Request an die Datenbank geht. Bei hundert Tabellen in der DB und hundert Tabellenobjekten in der Applikation, die alle beim Bootstrapping instanziiert werden, sind das hundert SQL-Queries mit immer denselben Ergebnissen und ohne die Gewissheit, dass auch wirklich alle Tabellenobjekte benutzt werden.

                                                    - Sven Rautenberg

                                                2. مرحبا

                                                  OOP wird nicht leichter, wenn man was anderes macht

                                                  Ja stimmt, du machst es mir aber auch nicht unbedingt leicht ;)

                                                  Dirk hat nur eine Funktion in seinem Artikel gehabt. Den in ein OOP-Beispiel umzusetzen ist nicht sehr leicht. Oder schwer - dann aber nicht wirklich OOP. :)

                                                  Frei nach deiner Empfehlung, alle Funktionen in Klassen zu packen :) So kleine helferlein, wie das Bsp. von Dirk, habe ich mittlerweile dutzende, aber eben nur als Funktionen.

                                                  1. Deine Klasse benötigt ein Konfigurationsobjekt, mit dem die Liste der User-Agent-Werte ausgelesen werden kann, und diese Klasse kann wiederum Unterobjekte benutzen, um sich z.B. an INI-Dateien, XML-Dateien oder Datenbanken anzudocken - oder auch ganz normale PHP-Arrays in Include-Dateien auslesen.

                                                  Ich versuche noch die Basics zu verinnerlichen, wie soll ich bei dieser Aufgabenstellung auch nur Ansatzweise durchsteigen?

                                                  1. Eigentlich sollte man hier lieber ein Request-Objekt haben, in dem die aktuellen Zustände des laufenden HTTP-Requests vom Browser gekapselt sind.

                                                  Also eine Klasse, die alle HTTP-Requests gespeichert hält? Wäre eigentlich Praktisch.

                                                  1. Am Ende noch ein wenig Codekritik zu den Namen der Methoden: Wir haben "isMobile" und "GetDevice" - einmal klein und einmal groß.

                                                  Soll ich einfach nur Konsequent bei einer Schreibweise bleiben, oder gibt es eine Empfohlene?

                                                  Ich habe mir nochmal die Menuklasse angesehen, wenn jetzt kein Iterator-Zeugs kommt, ist das, was du gepostet hast, ja Super einfach. Schwer wird es nur mit den ganzen zusatzaufgaben, die du hinten dranhängst.
                                                  Können wir nicht Schritt für Schritt arbeiten?

                                                  Ich würde die Klasse mit meinem Aktuellen Kenntnisstand so verwenden:

                                                  <?php  
                                                    
                                                  # Muss diese Klasse in eine Externe Datei, oder gehört diese Klasse direkt zur Klasse Navi?  
                                                  class Navi_Decorator  
                                                  {  
                                                    # Liste erzeugen  
                                                    public function buildList($id, $list)  
                                                    {  
                                                      return sprintf('<ul id="%s">%s</ul>', $id, $list);  
                                                    }  
                                                    # Links erzeugen  
                                                    public function getHtmlLink($link, $label)  
                                                    {  
                                                      return sprintf('<li><a href="%s">%s</a></li>', $link, htmlspecialchars($label));  
                                                    }  
                                                    # Link zur Aktiven Seite "entlinken"  
                                                    public function getHtmlActiveSite($label)  
                                                    {  
                                                      return sprintf('<li>%s</li>', htmlspecialchars($label));  
                                                    }  
                                                  }  
                                                    
                                                  class Navi  
                                                  {  
                                                    private $_links;  
                                                    private $_decorator;  
                                                    private $_activelink;  
                                                    
                                                    public function __construct(array $links, Navi_Decorator $decorator, $active)  
                                                    {  
                                                      $this->_links = $links;  
                                                      $this->_decorator = $decorator;  
                                                      $this->_activelink = $active;  
                                                    }  
                                                    
                                                    public function getHtmlMenu($id)  
                                                    {  
                                                      $menustring = '';  
                                                      foreach ($this->_links as $link => $label)  
                                                      {  
                                                        if ($this->_activelink == $link)  
                                                          $menustring .= $this->_decorator->getHtmlActiveSite($label);  
                                                        else  
                                                          $menustring .= $this->_decorator->getHtmlLink($link, $label);  
                                                      }  
                                                      return $this->_decorator->buildList($id, $menustring);  
                                                    }  
                                                  }  
                                                    
                                                  # Die erzeugung der Links kann ja erstmal warten, bis ich die vorherigen Prozesse verstanden habe  
                                                  $links = array('/'          => 'Home'  
                                                                ,'/impressum' => 'Impressum'  
                                                                ,'/kontakt'   => 'Kontakt');  
                                                  $decorator = new Navi_Decorator();  
                                                  $navi = new Navi($links, $decorator, $_SERVER['REQUEST_URI']); # REQUEST_URI zum entlinken der Aktiven Seite  
                                                  echo $navi->getHtmlMenu('mymenu'); # Parameter: <ul id="mymenu">LIST</ul>
                                                  

                                                  mfg

                                                  --
                                                  Die neuen 4 Jahreszeiten: Frühling, ARSCHLOCH, Herbst und Winter!
                            3. Moin!

                              Ich muss versuchen, die Neuerungen so gut es geht nach MVC-Pattern zu bauen, ohne dass es ausartet. Und die neuen Klassen müssen äußerst unkompliziert dann später durch Andere verwendbar sein.

                              Nein, das ist keine gute Taktik.

                              Entweder baust du das neue Modul nach modernen Erkenntnissen eigenständig neu "daneben" und fügst es über geeignete Ähnlichkeit in den URLs nach außen hin nahtlos ein. Dann wäre es allerdings Wahnsinn, dafür KEIN existierendes Framework zu nehmen.

                              Alternativ baust du mit möglichst minimalem Aufwand ein Modul zum existierenden Code dazu - dann aber solltest du dich bestmöglich an die existierenden Paradigmen des existierenden Codes halten, damit derjenige, der dann irgendwann mal das Update des Gesamtsystems machen will, nur genau EINE alte Methodik zu analysieren und neu umzusetzen hat, anstelle dass er pro Modul, wohlmöglich pro aufrufbarer URL, jeweils die individuelle Denkweise des jeweiligen Programmierers nachvollziehen muss.

                              Es kann natürlich sein, dass auch das existierende System sich schon durch maximale Heterogenität auszeichnet. In diesem Fall wär's vermutlich egal.

                              "Autoload" war da schon das richtige Stichwort. Ich muss jetzt nur möglichst sinnvoll entscheiden, wann tatsächlich Vererbung und wann nur Nutzung von anderen Klassen richtig ist.

                              Mit Pech hast du kein Autoload, auf dem du aufsetzen kannst. Und Hände weg von __autoload(), korrekt nutzt mal spl_autoload_register() - nur falls existierender Code das schon falsch macht, kommt es gern zu unschönen Konflikten, die man erst lösen muss.

                              - Sven Rautenberg

                3. Hi,

                  Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                  Zend Framework und Zend Engine sind unterschiedliche Dinge.

                  Bis die Tage,
                  Matti

                  1. مرحبا

                    Das ist ja soweit klar, meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                    Zend Framework und Zend Engine sind unterschiedliche Dinge.

                    Das leuchtet ein.

                    mfg

                4. Hallo,

                  Warum eigentlich noch Seperat runterladen?
                  Weil es ein Webframework ist das dir Helferfunktionen für Webapplikationen bietet ...

                  Das ist ja soweit klar,

                  das scheint mir aber nicht so.

                  meine Frage ist, warum ich die OnBoard-Installation von ZF nicht nutzen kann?

                  Da wird auf die Zend Engine verwiesen. Diese ist etwas anderes als das Zend Framework.

                  Den Unterschied zwischen diesen beiden versuchen jobo und Jeena Dir die ganze Zeit schon aufzuzeigen.

                  Freundliche Grüße

                  Vinzenz

                  1. مرحبا

                    Den Unterschied zwischen diesen beiden versuchen jobo und Jeena Dir die ganze Zeit schon aufzuzeigen.

                    Und der Groschen ist gefallen. Mir fehlte der Explizite Hinweis.

                    Schön, dass du auch wieder da bist, ist die Mannschaft ja fast Komplett :)

                    mfg

                    1. Hallo,

                      Schön, dass du auch wieder da bist,

                      Da schließe ich mich mal an. Trello  und http://wiki.selfhtml.org/wiki/SELFHTML:Unterstützer#Unterst.C3.BCtzer ist vermutlich nix für Dich, oder?

                      Gruß

                      jobo

        2. Tach!

          Wie ist das eigentlich mit dem ZF, wenn ich es einsetzen will, muss ich das Seperat installierem oder ist ZF mit PHP direkt verfügbar?

          Es ist kein Bestandteil PHPs. Es ist noch nichtmal so nah an PHP dran, dass es eine Subdomain unter php.net hat. Allerdings wird es von der Firma Zend betreut, die nicht unerheblich an der Entwicklung PHPs beteiligt ist.

          Ich habe bisher mit keiner PHP-Installation zutun gehabt, wo ZF nicht integriert war (phpinfo()).

          Dann hat es irgendwer vorsorglich installiert. Dass es in der phpinfo() zu sehen sein soll (außer einem Eintrag im include_path), wäre mir neu. Wenn du den Verweis auf die Zend Engine meinst - siehe oben.

          dedlfix.

          1. مرحبا

            Dann hat es irgendwer vorsorglich installiert.

            Aktuelle Xampp-Installation, auszug aus phpinfo() (PHP Version 5.3.8)

            This program makes use of the Zend Scripting Language Engine:
            Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

            Dass es in der phpinfo() zu sehen sein soll (außer einem Eintrag im include_path), wäre mir neu. Wenn du den Verweis auf die Zend Engine meinst - siehe

            Es ist nicht nur der Verweis auf Zend, s. o.
            Wobei bei der jetzigen PHP-Version auch kein Verweis mehr zu Zend steht, nur noch der Hinweis.

            mfg

            1. مرحبا

              Aktuelle Xampp-Installation, auszug aus phpinfo() (PHP Version 5.3.8)

              This program makes use of the Zend Scripting Language Engine:
              Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

              Einige Hoster haben es auch vorinstalliert

              Strato (per default)

              PHP Version 5.2.17
                This program makes use of the Zend Scripting Language Engine:
                ...

              1und1 (default)

              PHP Version 5.2.17
                This program makes use of the Zend Scripting Language Engine:
                ...

              Privater anbieter (default)

              PHP Version 5.3.5
                This program makes use of the Zend Scripting Language Engine:
                ...
                  with the ionCube PHP Loader v4.0.7, Copyright (c) 2002-2011, by ionCube Ltd.

              Wieso kann ich nicht die OnBoard-Installationen nutzen?

              mfg

              1. [latex]Mae  govannen![/latex]

                Einige Hoster haben es auch vorinstalliert

                OMG, sind das die aktuell installierten Versionen oder ist nur nicht die neueste aktiviert?

                Strato (per default)
                  PHP Version 5.2.17

                Stand Januar 2011

                1und1 (default)
                  PHP Version 5.2.1

                Stand Februar 2007(!)

                Privater anbieter (default)
                  PHP Version 5.3.5

                Immerhin „schon“ 5.3.x, dennoch Stand Januar 2011

                Aktuell sind übrigens 5.3.14 bzw 5.4.4

                Stur lächeln und winken, Männer!
                Kai

                --
                It all began when I went on a tour, hoping to find some furniture
                 Followed a sign saying "Beautiful Chest", led to a lady who showed me her best)
                SelfHTML-Forum-Stylesheet
                1. مرحبا

                  Einige Hoster haben es auch vorinstalliert
                  OMG, sind das die aktuell installierten Versionen oder ist nur nicht die neueste aktiviert?

                  Das sind die Einstellungen per Default.

                  Früher (als PHP4 noch Standard war) konnte man bei 1und1 mit der htaccess auf PHP5 umstellen, jetzt ist und bleibt die Version gleich: 5.2.17.

                  AddType x-mapp-php5 .php
                  AddHandler x-mapp-php5 .php

                  1und1 (default)
                    PHP Version 5.2.1
                  Stand Februar 2007(!)

                  Da fehlt eine Zahl.

                  PHP Version 5.2.17

                  mfg

                  1. [latex]Mae  govannen![/latex]

                    1und1 (default)
                      PHP Version 5.2.1
                    Stand Februar 2007(!)

                    Da fehlt eine Zahl.

                    PHP Version 5.2.17

                    Ok. Das macht es allerdings auch nicht viel besser, weil ab 5.3.x viel Neues hinzugekommen ist. Wenn man beim Programmieren auf all das verzichten muß, weil nur solche veralteten Versionen angeboten werden, taugt der Hoster einfach nicht.

                    Stur lächeln und winken, Männer!
                    Kai

                    --
                    It all began when I went on a tour, hoping to find some furniture
                     Followed a sign saying "Beautiful Chest", led to a lady who showed me her best)
                    SelfHTML-Forum-Stylesheet
                    1. مرحبا

                      Ok. Das macht es allerdings auch nicht viel besser, weil ab 5.3.x viel Neues hinzugekommen ist.

                      Bei welchem Hoster bist du und was ist dort per Default eingestellt?

                      mfg

            2. Tach!

              This program makes use of the Zend Scripting Language Engine:
              Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

              Zend Framework != Zend Engine

              Die Zend Engine ist sozusagen die Maschine, die den PHP-Code letztlich ausführt. Das Zend Framework ist PHP-Code, der dir ermöglicht Applikationen nach MVC-Pattern aufzubauen.

              dedlfix.

      2. Hallo,

        Ist das schöner?

        Oh jaaa!

        Würde mich mal interessieren, warum.

        Der screencast ist zwar schon ein wenig in die Jahre gekommen, dennoch immer noch sehr beeindruckend:

        http://media.rubyonrails.org/video/rails_blog_2.mov

        Bin jetzt kein Zend-Framework Fan, aber da ich das nicht professionell mache, bin ich da hängen geblieben, weil ich das alles sehr vernünftig fand:
        http://framework.zend.com/manual/de/zend.layout.quickstart.html

        Das ist sicherlich alles Vernünftig, wenn man es auch für vernünftig hält PHP als Programmiersprache zu nutzen.

        Ich dachte, viel "schöner" (;-) gehts nicht.

        Du hast noch viel zu lernen ;-) Vor allem die Möglichkeit in Ruby code-blocks als argument an funktionen zu übergeben macht den Code so unglaublich viel einfacher zu lesen, es wird fast schon wie Englisch. Überhaupt ist Ruby so viel einfacher als PHP, es ist konsistent (alles ist ein Objekt, ja sogar Zahlen und boleans, etc.) so sachen wie die Reihenfolge der argumente ist konsistent, die namen der Methoden sind konsistent, es gibt sinnvolle Namespaces, etc.

        Ich wünschte ich hätte als erste Sprache nicht PHP sondern Ruby, Java oder sogar JavaScript gelernt, ich hätte mir so viel einfacher getan.

        Jeena

  2. Moin!

    Ich habe da nun eine generelle Frage zur Philosophie. Es gibt in vielen Modulen ja immer die Qual, dass man sowohl Funktionalitäten für das Frontend, als auch Administrationsfunktionen für das Backend bereitstellen muss.

    Wie trennt Ihr das? Gefordert ist, dass die Funktionen alle in einem Modul (einer Klasse) zusammengefasst werden, mit Ausnahme natürlich von Darstellungs-, Datenhaltungs-, oder Berechtigungsaufgaben, usw., die sich selbstverständlich in anderen Klassen befinden.

    Das mit "ein Modul === eine Klasse" ist Bullshit. Wer sowas fordert, hat von OOP noch weniger Ahnung, als du. ;)

    Klassen bzw. Objekte baut man ja genau deshalb, damit man die Aufgabe bestmöglich abstrahieren kann. Wenn man sämtliche Funktionen nun in genau eine Klasse integrieren muss, kann man nicht mehr gut abstrahieren, sondern muss im Prinzip prozedural programmieren - nur etwas besser abgeschottet innerhalb eben dieser Klasse.

    Ich möchte nicht immer den gesamten Klotz laden lassen, obwohl doch meistens nur die Userfunktionen benötigt werden.

    Autoloading hast du dir angeschaut? Unbedingt implementieren, dazu gibts auch einen PHP-Standard namens "PSR-0". Dadurch sparst du dir zum einen das andauernde require_once überall (was auch nicht so wahnsinnig performance-fördernd ist), zum anderen reduziert sich der Memory-Footprint der Applikation ohne dein Zutun schon mal signifikant, weil nur noch die tatsächlich benutzten Klassen-Deklarationen geladen werden. Und wenn man die Klassen hinreichend klein gestaltet...

    Auf der anderen Seite sind Performance-Argumente irrelevant, solange ein Performance-Problem noch nicht wahrgenommen werden kann. Selbst wenn diese Modul-Klasse "groß" ist, so lagert sie eben doch insgesamt im Opcode-Cache und ist schnell verfügbar.

    - Sven Rautenberg

    1. Hello,

      Ich habe da nun eine generelle Frage zur Philosophie. Es gibt in vielen Modulen ja immer die Qual, dass man sowohl Funktionalitäten für das Frontend, als auch Administrationsfunktionen für das Backend bereitstellen muss.

      Wie trennt Ihr das? Gefordert ist, dass die Funktionen alle in einem Modul (einer Klasse) zusammengefasst werden, mit Ausnahme natürlich von Darstellungs-, Datenhaltungs-, oder Berechtigungsaufgaben, usw., die sich selbstverständlich in anderen Klassen befinden.

      Das mit "ein Modul === eine Klasse" ist Bullshit. Wer sowas fordert, hat von OOP noch weniger Ahnung, als du. ;)

      *haha* Der Kunde hat an dem Projekt inzwischen sieben Vorgänger verschlissen. Das wird richtig[tm] Spaß machen. Aber ich kann es mir auch nicht immer aussuchen.

      Ich möchte nicht immer den gesamten Klotz laden lassen, obwohl doch meistens nur die Userfunktionen benötigt werden.

      Autoloading hast du dir angeschaut? Unbedingt implementieren, dazu gibts auch einen PHP-Standard namens "PSR-0". Dadurch sparst du dir zum einen das andauernde require_once überall (was auch nicht so wahnsinnig performance-fördernd ist), zum anderen reduziert sich der Memory-Footprint der Applikation ohne dein Zutun schon mal signifikant, weil nur noch die tatsächlich benutzten Klassen-Deklarationen geladen werden. Und wenn man die Klassen hinreichend klein gestaltet...

      Das schaue ich mir genauer an. Ich müsste dann ja nur eine Klasse bauen, die die anderen benutzt.
      Dann müssten die Profis vom Kunden ja doch immer nur die passende Klasse einbinden und benutzen. Das weitere Teile (andere Klassen) nach Bedarf nachgeladen werden, müssen die gar nicht (aktiv) wissen.

      Der Arbeitsspeicher wird schon knapp, auch wenn der Prozessor noch nicht schwitzt.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
  3. hi Tom,

    Ich möchte nicht immer den gesamten Klotz laden lassen, obwohl doch meistens nur die Userfunktionen benötigt werden.

    Es gibt genau zwei (*) Möglichkeiten, Beziehungen zwischen Klassen herzustellen:

    • Vererbung
    • Delegation einzelner Methoden fremder Klassen in eigene Klassen

    Damit teilst Du den Code auf und definierst Schnittstellen.

    *Anm.: Es gibt sicher auch noch andere Möglichkeiten, aber solchen Code kannst Du hinterher nicht mehr bearbeiten.

    Und übertreibe es nicht mit der Vererbung, i.d.R. reicht eine Subklasse.

    Hotti

    1. Tach!

      • Delegation einzelner Methoden fremder Klassen in eigene Klassen

      Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.

      dedlfix.

      1. Hallo,

        • Delegation einzelner Methoden fremder Klassen in eigene Klassen
          Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.

        Das hat nix mit der Sprache zu tun, das ist ein Design Pattern, und zwar auch ein sehr gutes.

        http://en.wikipedia.org/wiki/Delegation_pattern

        Jeena

        1. Tach!

          • Delegation einzelner Methoden fremder Klassen in eigene Klassen
            Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.
            Das hat nix mit der Sprache zu tun, das ist ein Design Pattern, und zwar auch ein sehr gutes.
            http://en.wikipedia.org/wiki/Delegation_pattern

          Das ist nichts großartig anderes als der Aufruf einer Funktion oder einer Methode eines anderen Objekts. Dabei wird die Aufgabenerledigung delegiert und nicht Methoden in andere Klassen eingebunden, so wie hotti das gesagt hat. Zudem kann man vom instantiierten Objekt auch alle anderen öffentlichen Mitglieder verwenden und nicht nur ein einzelnes. Konkret gesagt könnte gemäß dem ersten Wikipedia-Beispiel (Java) die Klasse Printer auf sämtliche Mitglieder von RealPrinter zugreifen, wenn dieses noch mehr davon hätte als die eine Methode.

          Das Problem bei hotti ist, ich weiß meist nicht, ob er sich nur falsch ausdrückt oder was falsches oder unpassendes geantwortet hat. Er verwendet zu oft sein Perl-Wissen (was anderen zufolge auch nicht gerade das beste ist) und stülpt das dann PHP über, ohne die Gegebenheiten PHPs genauer zu kennen. Es ist schwierig darauf einzugehen. Es unkommentiert stehen zu lassen, ist für Wenigerwissende nicht hilfreich. Einfach nur "Falsch!" zu sagen ebensowenig. Und da er meist recht viele Fachbegriffe verwurstet, artet das oft in umfangreiche Arbeit aus, wenn ich auf alles einzugehen und klarzustellen versuche.

          dedlfix.

          1. Hallo,

            Das ist nichts großartig anderes als der Aufruf einer Funktion oder einer Methode eines anderen Objekts. Dabei wird die Aufgabenerledigung delegiert und nicht Methoden in andere Klassen eingebunden

            Exakt, nicht mehr und nicht weniger.

            Das Problem bei hotti ist, ich weiß meist nicht, ob er sich nur falsch ausdrückt oder was falsches oder unpassendes geantwortet hat. Es unkommentiert stehen zu lassen, ist für Wenigerwissende nicht hilfreich. Einfach nur "Falsch!" zu sagen ebensowenig. Und da er meist recht viele Fachbegriffe verwurstet, artet das oft in umfangreiche Arbeit aus, wenn ich auf alles einzugehen und klarzustellen versuche.

            Ok, verstehe.

            Jeena

      2. hi,

        • Delegation einzelner Methoden fremder Klassen in eigene Klassen

        Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.

          
        class XR{  
        	public function xr($haystack = '', $stash = array()){  
        		if(!sizeof($stash)){ return $haystack; }  
        		$needles = array_map(create_function('$e','return("%$e");'), array_keys($stash));  
        		$s = str_replace($needles, $stash, $haystack);  
        		return $s;  
        	}  
        }  
          
        class Foo{  
        	public function __construct(){  
        		$this->XR = new XR;  
        	}  
        }  
          
        date_default_timezone_set('Europe/Paris');  
        $foo = new Foo;  
        print $foo->XR->xr("Heute ist %tag\n", array('tag' => date('D'))); # Heute ist Tue  
        
        
        1. Hallo,

          $foo = new Foo;
          print $foo->XR->xr("Heute ist %tag\n", array('tag' => date('D'))); # Heute ist Tue

            
          Nope, das ist kein (gutes) Beispiel für ein delegate. Ich probier mal mein Glück, vielleicht hilft es jemandem  
            
          ~~~php
          <?php  
          /* Printer interface and implementations */  
            
          interface iPrinter {  
              public function print();  
              public function getInfo();  
          }  
            
          class NormalPrinter implements iPrinter {  
              private $printer;  
            
              function __construct() {  
                  $this->printer = printer_open("HP Deskjet 930c");  
              }  
            
              function __destruct() {  
                  printer_close($this->printer);  
              }  
            
              public function print($content) {  
                  printer_write($this->printer, $content);  
              }  
            
              public function info() {  
                  return "HP Deskjet 930c";  
              }  
          }  
            
          class VirtualPrinter implements iPrinter {  
            
              public function print($content) {  
                  echo $content;  
              }  
            
              public function info() {  
                  return "A stdout printer";  
              }  
          }  
            
          /* */  
            
          class Calculator {  
              public $printerDelegate;  
            
              function sum($a, $b) {  
                  $this->print("{$a} + {$b} = {$a + $b}");  
              }  
            
              function difference($a, $b) {  
                  $this->print("{$a} - {$b} = {$a - $b}");  
              }  
            
              private function print($content) {  
                  if ($this->printerDelegate) {  
                      $this->printerDelegate->print($content . "\n");  
                  }  
              }  
          }  
            
            
          // Testausführung  
            
          $printer = new NormalPrinter();  
            
          $calculator = new Calculator();  
          $calculator->printerDelegate = $printer; // Wir setzen den normalen Drucker als delegate ein  
            
          $calculator->sum(2, 5); // Druckt 7 auf dem HP Deskjet aus  
          $calculator->difference(10, 4); // Druckt 6 auf dem HP Deskjet aus  
            
          $virtualPrinter = new VirtualPrinter();  
          $calculator->printerDelegate = $virtualPrinter; // Wir ändern den Delegate zu einem virtuellen Drucker  
            
          $calculator->sum(2, 5); // Der gleiche Aufruf wie oben gibt 7 auf stdout aus  
          $calculator->difference(10, 4); // Der gleiche Aufruf wie oben gibt 6 auf stdout aus  
          
          

          Jeena

          1. Hello,

            $printer = new NormalPrinter();

            $calculator = new Calculator();

            Spätestens hier würde ich als Teamleiter laut aufschreien. Weißt Du, warum?

            $calculator->printerDelegate = $printer; // Wir setzen den normalen Drucker als delegate ein

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Hallo,

              Spätestens hier würde ich als Teamleiter laut aufschreien. Weißt Du, warum?

              $calculator->printerDelegate = $printer; // Wir setzen den normalen Drucker als delegate ein

              Weil es keine PHP-Syntax ist oder so was? Es ging mir darum das Pattern nachvollziehbar darzustellen, nicht konkrete PHP-Syntax nachzuschlagen, das kann ja jeder selbst.

              Jeena

              1. Tach!

                Spätestens hier würde ich als Teamleiter laut aufschreien. Weißt Du, warum?

                $calculator->printerDelegate = $printer; // Wir setzen den normalen Drucker als delegate ein

                Weil es keine PHP-Syntax ist oder so was?

                Es ist gültige PHP-Syntax. Vermutlich meint Tom, dass du öffentlichen Eigenschaften direkt Werte zuweist, statt über einen Setter zu gehen. Letzterer ist vor allem dann sinnvoll, wenn weitere Funktionalität beim Zuweisen gewünscht ist. Das kann zum Bespiel eine Prüfung des Arguments sein und sei es, dass man per Type-Hinting nur bestimmte Klassen zulässt. Ohne Prüfung ist es nur ein sinnloser Funktionsaufruf (den man vielleicht später noch erweitert oder auch YAGNI).

                dedlfix.

                1. Hallo,

                  Es ist gültige PHP-Syntax. Vermutlich meint Tom, dass du öffentlichen Eigenschaften direkt Werte zuweist, statt über einen Setter zu gehen.

                  Es gibt doch mittlerweile sogar Setter und Getter in PHP, so dass man das falls gebraucht im Nachhinein ändern könnte ohne dass jemals jemand seinen Code der die Klasse benutzt ändern müsste, oder nicht?

                  class Calculator {  
                      private $_printerDelegate;  
                    
                      function __get($name) {  
                          if ($name == "printerDelegate") {  
                              return $this->_printerDelegate;  
                          }  
                      }  
                    
                      function __set($name, $value) {  
                          if ($name == "printerDelegate") {  
                              $this->_printerDelegate = $value;  
                          }  
                      }  
                    
                      function sum($a, $b) {  
                          $this->print("{$a} + {$b} = {$a + $b}");  
                      }  
                    
                      function difference($a, $b) {  
                          $this->print("{$a} - {$b} = {$a - $b}");  
                      }  
                    
                      private function print($content) {  
                          if ($this->_printerDelegate) {  
                              $this->_printerDelegate->print($content . "\n");  
                          }  
                      }  
                  }
                  

                  Jeena

                  1. Moin!

                    Es ist gültige PHP-Syntax. Vermutlich meint Tom, dass du öffentlichen Eigenschaften direkt Werte zuweist, statt über einen Setter zu gehen.
                    Es gibt doch mittlerweile sogar Setter und Getter in PHP, so dass man das falls gebraucht im Nachhinein ändern könnte ohne dass jemals jemand seinen Code der die Klasse benutzt ändern müsste, oder nicht?

                    Die magischen Methode sollte man nach Möglichkeit immer vermeiden zu benutzen. Sie sind sehr schwer testbar, insbesondere wenn sie schlecht geschrieben sind (so wie hier im Beispiel), weil sie sich nur um den Positiv-Fall kümmern. Darüber hinaus erschweren sie die Nutzung von Code-Completion in handelsüblichen Entwicklungsumgebungen.

                    class Calculator {

                    private $_printerDelegate;

                    function __get($name) {
                            if ($name == "printerDelegate") {
                                return $this->_printerDelegate;
                            }
                        }

                    function __set($name, $value) {
                            if ($name == "printerDelegate") {
                                $this->_printerDelegate = $value;
                            }
                        }

                    //[...]

                    }

                      
                     - Sven Rautenberg
                    
                    1. Hallo,

                      Die magischen Methode sollte man nach Möglichkeit immer vermeiden zu benutzen.

                      A bold statement.

                      Sie sind sehr schwer testbar, insbesondere wenn sie schlecht geschrieben sind (so wie hier im Beispiel), weil sie sich nur um den Positiv-Fall kümmern.

                      Hm, da hast du wohl Recht, sie sollten sich für alles andere verhalten als ob sie nicht da wären. Ich hab noch mal in die PHP-Doku geschaut und muss wohl einsehen dass auch dieser Teil von PHP einfach nur scheiße ist, das schön zu Implementieren scheint ja ein enormer Aufwand zu sein.

                      Andere Sprachen haben das so unglaublich viel schöner gelöst wie @property in Objective-C wo man getter und setter automatisch erstellen lässt und diese dann aber auch einzeln überschreiben kann. Oder Ruby mit attr_accessor (samt attr_reader und attr_writer) die das genauso machen dass sie getter und setter methoden automatisch erstellen, die man dann aber noch von hand überschreiben kann falls benötigt.

                      Darüber hinaus erschweren sie die Nutzung von Code-Completion in handelsüblichen Entwicklungsumgebungen.

                      Naja ok, so was ist mit PHP eh ein Krampf da schon alleine die Typen fehlen.

                      Jeena

                      1. Tach!

                        Darüber hinaus erschweren sie die Nutzung von Code-Completion in handelsüblichen Entwicklungsumgebungen.
                        Naja ok, so was ist mit PHP eh ein Krampf da schon alleine die Typen fehlen.

                        Mit der richtigen Entwicklungsumgebung[*] ist das kein großartiges Problem, die kennt einen phpdoc-ähnlichen einzeiligen Kommentar, der bei Typunklarheiten explizit einen vorgeben kann. Und das ist auch für den menschlichen Leser sehr hilfreich.

                        [*] Zend Studio auf alle Fälle, die Eclipse-Plugind sollten es auch können.

                        dedlfix.

                2. Hello,

                  Weil es keine PHP-Syntax ist oder so was?

                  Es ist gültige PHP-Syntax. Vermutlich meint Tom, dass du öffentlichen Eigenschaften direkt Werte zuweist, statt über einen Setter zu gehen. Letzterer ist vor allem dann sinnvoll, wenn weitere Funktionalität beim Zuweisen gewünscht ist. Das kann zum Bespiel eine Prüfung des Arguments sein und sei es, dass man per Type-Hinting nur bestimmte Klassen zulässt. Ohne Prüfung ist es nur ein sinnloser Funktionsaufruf (den man vielleicht später noch erweitert oder auch YAGNI).

                  Stimmt. Der Sinn von Delegegationen ist ja gerade, dass man nur an Klassen delegiert, die auch zuständig sind. Hier könnte man jeden Schwachsinn eintragen. Ob bei PHP hier bei der Zuweisung überhaupt eine Typprüfung als niedrigste Hürde stattfindet, habe ich jetzt auch nicht gestestet, könnte mir aber vorstellen, dass noch nicht einmal das passiert.

                  Liebe Grüße aus dem schönen Oberharz

                  Tom vom Berg

                  --
                   ☻_
                  /▌
                  / \ Nur selber lernen macht schlau
                  http://bergpost.annerschbarrich.de
                  1. Tach!

                    Ob bei PHP hier bei der Zuweisung überhaupt eine Typprüfung als niedrigste Hürde stattfindet, habe ich jetzt auch nicht gestestet, könnte mir aber vorstellen, dass noch nicht einmal das passiert.

                    Variablen sind ja generell nicht typgebunden, also kann man denen zuweisen, was man will. Type Hinting ist nur bei der Übergabe von Funktionsparametern vorhanden.

                    dedlfix.

            2. Moin!

              Hello,

              $printer = new NormalPrinter();

              $calculator = new Calculator();

              Spätestens hier würde ich als Teamleiter laut aufschreien. Weißt Du, warum?

              $calculator->printerDelegate = $printer; // Wir setzen den normalen Drucker als delegate ein

              Dependency Injection bzw. Inversion of Control!

              Wenn der Calculator zum Funktionieren einen Printer benötigt, dann sollte man ihn nicht instanziieren dürfen, ohne ihm ein Printer-Objekt mitzugeben, welches das notwendige Interface implementiert.

              Also:

              $calculator = new Calculator(new NormalPrinter());

              An dieser Stelle kann man natürlich diskutieren, ob sowas rein für die Anwendung selbst sinnvoll ist. Aber spätestens wenn es an's Testen geht, wird man eben gerade KEIN echtes Papier verdrucken wollen, nur um das korrekte Funktionieren des Rechners zu prüfen. Man will auch keine Ausgabe auf STDOUT wieder einfangen und ggf. parsen. Man will ein Mock-Objekt an den Calculator geben, welches ebenfalls das gewünschte Interface implementiert, aber intern nichts druckt, sondern nur die Aufrufe seiner Methoden inkl. Parameter registriert und eventuell auch Rückgabewerte liefert - und im Test jeweils entsprechend konfiguriert ist.

              - Sven Rautenberg

        2. Tach!

          • Delegation einzelner Methoden fremder Klassen in eigene Klassen
            Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.
            [...Code...]

          Wenn du das gemeint hast, hast du was anderes geschrieben. Objekte zu instantiieren und deren Mitglieder zu nutzen, ist wie die Erteilung eines Auftrag an eine externe Firma, auf dass sie etwas liefere. Deine Aussage war vergleichsweise, dass ein Mitarbeiter der externen Firma ins Haus kommt und mehr oder weniger in die hiesigen Strukturen integriert arbeitet.

          dedlfix.

        3. hi,

          das ist die Erklärung "Delegation":

          print $foo->XR->xr("Heute ist %tag\n", array('tag' => date('D'))); # Heute ist Tue

          In der Praxis sollte jedoch nicht mit der Instanz auf ein Attribut direkt zugegriffen werden, sondern über einen Accessor. Der direkte Zugriff auf ein Attribut ist gemäß der AGB zu OOP jedoch in der Klasse selbst erlaubt (dafür gibt es Gründe aber auch Ausnahmen). Hier nun die komplette Delegierung einer Funktion xr();

            
          // Fremde Klasse  
          XR  
          // mit einer begehrenswerten Methode  
          xr()  
            
          // Meine Klasse will die Methode der fremden Klasse nutzen über die  
          // eigene Instanz  
          class Foo{  
          	public function __construct(){  
          		// Mache eine Instanz der fremden Klasse zum eigenen Attribut  
          		$this->XR = new XR;  
          	}  
            
          	// Mache die Methode der fremden Klasse zu einer  
          	// eigenen Methode (gleichnamig)  
          	public function xr($haystack = '', $stash = array()){  
          		// verwende dieselben Argumente  
          		// und das Objekt der fremden Klasse (hier im Attribut XR)  
          		return $this->XR->xr($haystack, $stash);  
          	}  
            
          }  
            
          date_default_timezone_set('Europe/Paris');  
          $foo = new Foo; // Instanz meiner Klasse  
          print $foo->xr("Heute ist %tag\n", array('tag' => date('D'))); # Heute ist Tue  
          
          

          Und ja, Ihr könnt das Alles noch viiiiiiiiiiel besser machen, ich plaudere hier nur aus meiner täglichen Praxis, wo es auch gar nicht mehr darauf ankommt, den Code der fremden Klasse zu kennen, es genügt, die Methode zu kennen und wissen, welche Parameter erforderlich sind.

          Hotti

          --
          Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
      3. Hi,

        • Delegation einzelner Methoden fremder Klassen in eigene Klassen

        Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.

        mit ein wenig Phantasie könnte man darunter traits verstehen.

        Bis die Tage,
        Matti

        1. Tach!

          • Delegation einzelner Methoden fremder Klassen in eigene Klassen
            Themenbereich ist PHP, nicht Perl. Erkläre doch bitte mal, wie das unter PHP geht.
            mit ein wenig Phantasie könnte man darunter traits verstehen.

          Mitunter braucht man zu viel Phantasie, um seine Antworten deuten zu können. Traits wird er sicher nicht gemeint haben. Diese werden komplett eingebunden mit nicht nur deren Methoden sondern auch deren Eigenschaften. Und Klassen sind es auch nicht. Selbst wenn er irgendwelche allgemeinen Dinge antwortet, wäre es sehr hilfreich, wenn er das dann auf die konkret gefragt Umgebung beziehen könnte.

          dedlfix.

  4. Hallo,

    hast Du Dir mal Zend-Framework und Co angeschaut. Da kann man sich eigentlich sehr schön abgucken, wie man das gescheit alles machen kann.

    Gruß

    jobo

  5. hi,

    Wie trennt Ihr das? Gefordert ist, dass die Funktionen alle in einem Modul (einer Klasse) zusammengefasst werden, mit Ausnahme natürlich von Darstellungs-, Datenhaltungs-, oder Berechtigungsaufgaben, usw., die sich selbstverständlich in anderen Klassen befinden.

    In meinem Framework (Perl) mache ich das so, dass ich über ein URL-Map den Locator an eine Subklasse binde. Das ergibt einen äußerst schlanken und aufgeräumten Code, weil damit in der Subklasse nur wenige, i.d.R. zwei Methoden zu schreiben sind, alles Andere (A.) wird von der Basisklasse erledigt.

    A.: Darstellung, Templateprozess, Prüfen der Berechtigungen, Suchfunktion, Indizierung, Navigation....

    Weitere Trennungen der Responseklassen erfolgen über den Inc-Pfad im Dateisystem (Domain => Admin/User).

    Für Code, der nur bei Bedarf kompiliert werden muss, setze ich Autoloader ein.

    Hotti

    --
    Beispiel einer Responseklasse (letzter Abschnitt)