MB: Single-Responsibility-Prinzip für PHP?

moin community,

in PHP habe ich alles strikt in Methoden nach aufgabe sortiert, nix gemischt und es ist IMO sauber. Das ist ein schönes Prinzip das ich beibehalten möchte. Ein berufserfahrener Kommentator meine "funktionsaufrufe sind teuer". Ich hab sowas geahnt desfegen meine Frage: Kommrt PHP mit diesem SRP klar oder sollte man das bei komplexen Systemen zugunsten der Perfomance mischen?

vlg MB

  1. Hi,

    meiner Meinung (als Laie) nach hängt das von der Situation ab, allgemein würde ich es so lassen, aber mir die Freiheit lassen, gegen das Prinzip im speziellen Fall zu verstoßen, falls der Verstoß für die Optimierung und die Situation nötig ist.

    Grüße

  2. Hallo MB,

    leider ist das so. PHP 5 hat einen ziemlich großen Overhead bei Funktionsaufrufen. Das ist in PHP 7 - nach einem Mikrobenchmark, den ich gerade gemacht habe - deutlich schneller geworden. PHP 5.6 brauchte für 10 Millionen Aufrufe 17 Sekunden, PHP 7.1 dagegen 0,4s. Die Laufzeit der leeren Schleife ist herausgerechnet. Es ist also ca. Faktor 50 schneller geworden.

    Methodenaufrufe sind ein klein wenig langsamer als direkte Funktionen - in PHP 5 und 7. In PHP 7 scheint die Differenz aber geringer zu sein.

    Aber Vorsicht: Das ist ein Mikrobenchmark gewesen, der Funktionen verwendete die aus einem RETURN-Statement bestanden. Möglicherweise ist das nicht repräsentativ. Mach deinen eigenen - verwende microtime(false) zum Festhalten von Start- und Endzeit und gib Dir die Laufzeitdifferenz aus. Allerdings musst Du solche Messungen oft wiederholen - die erwähnten 10 Millionen Durchläufe waren es bei mir - weil Computers Kleinhirn so verflixt flott ist.

    Ein Satz, den ich bei Stackoverflow dazu mal gelesen habe, lautet: "Wenn Du Dir über den Overhead von Funktionsaufrufen einen Kopf machen musst, ist PHP nicht das richtige Tool für dein Problem". Der stammt aber aus der PHP 5 Zeit.

    Aufwändige Frameworks mit feingranularer Methodenstruktur sind nicht nur in PHP ein Performanceproblem. Java und C# gehen da genauso in die Knie, vor allem dann, wenn viele virtuelle Methoden im Spiel sind. In PHP sind ALLE Methoden virtuell, d.h. PHP kann keine Methodenaufrufe inline umwandeln.

    Du musst deswegen jetzt nicht von Hand anfangen, aus Methodenaufrufen inline-Code zu machen. SRP ist aus architektonischer Sicht eine gute Sache.

    Aber wirf auch mal einen Blick auf Abschnitt 1 in diesem Artikel. Die Wichtigkeit solcher Mikrooptimierungen hängt davon ab, wie Dir Deine Computerleistung berechnet wird.

    Rolf

    1. moin RolfB,

      leider ist das so. PHP 5 hat einen ziemlich großen Overhead bei Funktionsaufrufen.

      :'(

      Das ist in PHP 7 - nach einem Mikrobenchmark, den ich gerade gemacht habe - deutlich schneller geworden.

      neben bei: Wie macht man sowas? gibts n tool dafür oder muss man sich das tool in php selber programmieren.

      PHP 5.6 brauchte für 10 Millionen Aufrufe 17 Sekunden, PHP 7.1 dagegen 0,4s.

      😱 das ist sehr schnell.

      In PHP sind ALLE Methoden virtuell, d.h. PHP kann keine Methodenaufrufe inline umwandeln.

      was heist das "Viertuelle Methoden"?

      Aber wirf auch mal einen Blick auf Abschnitt 1 in diesem Artikel.

      Leichter gesagt als getan 😕. Danke Nochmals!

      vlg MB

      1. Das ist in PHP 7 - nach einem Mikrobenchmark, den ich gerade gemacht habe - deutlich schneller geworden.

        neben bei: Wie macht man sowas? gibts n tool dafür oder muss man sich das tool in php selber programmieren.

        Das geht schnell. Pseudocode:

        start=microtime
        
        1 Mio mal: do
           testcode
        done
        
        echo (start-microtime)
        
        

        Alternative:

        Skript:

        1 Mio mal: do
           testcode
        done
        
        

        Linux-Terminal:

        ~> time php Skript
        
        1. start=microtime
          
          1 Mio mal: do
             testcode
          done
          
          echo (start-microtime)
          
          

          Misst die Zeit für das Ausführen des bereits geparsten, je nach Sprache auch kompilierten Testcodes.

          Alternative:

          Skript:

          1 Mio mal: do
             testcode
          done
          

          Linux-Terminal:

          ~> time php Skript
          

          Misst die Zeit für den Aufruf und Start des Interpreters, das Parsen und Kompilieren des Quellcodes und das Aufräumen am Ende.

          Fazit:

          Beide Methoden verwenden, über die Ergebnisse, deren Grundlagen (z.B. wird PHP als Modul oder als CGI ausgeführt? Was wird gecacht?) und deren Gewichtung nachdenken.

      2. Hallo MB,

        wie man solche Benchmarks schreibt, hat Regina Schaukrug erläutert. Ich mache sowas zweistufig:

        // 1. Laufzeit der leeren Schleife bestimmen
        $start = microtime(false);
        for ($i=0; $i<10000000; $i++)
        {
        }
        $emptyLoop = microtime(false) - $start;
        
        $start = microtime(false);
        for ($i=0; $i<10000000; $i++)
        {
           // zu messender code
        }
        $laufzeit = microtime(false) - $start - $emptyloop;
        

        Das geht natürlich schief, wenn man einen Compiler hat, der eine leere Schleife erkennt und wegoptimiert 😀

        Was ist eine virtuelle Methode. Ich mache ein Beispiel:

        class A
        {
           public function Hugo() {
              return "Hugo in ".get_class($this)." ruft Otto: " . $this->Otto();
           }
           public function Otto() {
              return "Schnauze!";
           }
        }
        
        class B extends A
        {
           public function Willi() {
              return "Willi in ".get_class($this)." ruft Otto: " . $this->Otto();
           }
           public function Otto() {
              return "Hallo, mein Lieber!";
           }
        }
        
        $a = new A();
        $b = new B();
        
        echo $a->Hugo() . "<br>\n";
        echo $b->Hugo() . "<br>\n";
        echo $b->Willi() . "<br>\n";
        

        Ergebnis ist:

        Hugo in A ruft Otto: Schnauze!<br>
        Hugo in B ruft Otto: Hallo, mein Lieber!<br>
        Willi in B ruft Otto: Hallo, mein Lieber!<br>
        

        Warum ist das bemerkenswert? Hugo ist eine Methode der Klasse A. Ein sturer Compiler (C, C++, C#) würde sagen: Hugo ruft Otto, Otto ist eine Methode von A, also ruft Hugo IMMER A::Otto auf. Das ist ein statisch gebundener Methodenaufruf. Die zweite Ausgabezeile würde dann lauten: Hugo in B ruft Otto: Schnauze!.

        Ist eine Methode virtuell, dann wird erst zur Laufzeit geprüft, welche Klasse das Objekt nun wirklich hat. $a->Hugo() ruft Hugo auf einem Objekt der Klasse A auf, darum wird A::Otto aufgerufen. $b->Hugo() ruft Hugo auf einem Objekt der Klasse B auf, darum wird B::Otto gerufen. Das ist ein virtuell gebundener Methodenaufruf. Um sowas machen zu können, darf der Compiler den Aufruf $this->Otto() in Hugo nicht in Inline-Code umwandeln (er würde dann einfach den Code von Otto in Hugo hineinkopieren), sondern muss in dem Moment, wo aufgerufen wird, gucken, welcher Otto es denn nun wirklich ist. Das ist langsamer.

        In C, C++ und C# kann ich dem Compiler sagen, ob ich eine virtuelle oder statische Bindung haben möchte. Java, PHP und JavaScript kann ich das nicht sagen, die binden immer virtuell. Da kann ich mittels "final" Schlüsselwort höchstens sagen, dass eine Methode in abgeleiteten Klassen nicht neu definiert werden darf.

        Rolf

        --
        Dosen sind silbern
  3. Nun,

    SRP ist keine Frage die sich unmittelbar an die Programmiersprache richtet

    in PHP habe ich alles strikt in Methoden nach aufgabe sortiert,

    und das geht am Verständnis für SRP völlig vorbei.

    Ein berufserfahrener Kommentator meine "funktionsaufrufe sind teuer".

    Ja natürlich ist es ein Unterschied ob eine Konfiguration rekursiv durchlaufen werden muss um einen bestimmten Wert da rauszufischen oder ob dieser Wert direkt adressierbar ist. Aber das hat ja mit SRP nichts zu tun.

    Vielmehr bescheibt SRP eine Art und Weise der Verteilung der Verantwortlichkeiten. Nicht die Konfiguration ist verantwortlich wenn Eigenschaften verändert werden sollen sondern die Anwendung (als Beispiel).

    Und vergiss endlich diese elenden Design Pattern Spezifikationen der Go 4. Praktische Programmierung ist weitaus vielfältiger, nicht umsonst prägt OOP den Begriff der Polymorphie. MfG

    1. moin pl,

      Und vergiss endlich diese elenden Design Pattern Spezifikationen der Go 4. Praktische Programmierung ist weitaus vielfältiger, nicht umsonst prägt OOP den Begriff der Polymorphie.

      Weiser Rat den ich nicht befolgen werde. Warum soll ich das Rat mühsam neu erfinden.

      Klar, dein Rat bietet sehr viele Vorteile: Ein Problem erfahrbar zu machen und lösungen zu erarbeiten, trägt extrem viel zum Lernprozess bei.

      Ich halte mich an "ein mögliches Problem anzusprechen und vorab professionelle Lösungen an die Hand zu geben". Aber Ich muss mir halt eingestehen, das ich niemals so tief drin sein werde, wie wenn ich deinen Ratschlag befolgen würde und dann aber auch sehr viel länger dafür brauchen würde. Anders ausgedrückt "das ist wie wenn ich in der Matheklausur abschreibe".

      vlg MB

      1. hi,

        Und vergiss endlich diese elenden Design Pattern Spezifikationen der Go 4. Praktische Programmierung ist weitaus vielfältiger, nicht umsonst prägt OOP den Begriff der Polymorphie.

        Weiser Rat den ich nicht befolgen werde. Warum soll ich das Rat mühsam neu erfinden.

        Ein Programmierer erfindet mit jedem Programm das Rad neu. Wenn er das nicht machen würde, wäre er kein Programmierer. Oder anders ausgedrückt: Ein Programmierer wird niemals mit Sicherheit sagen können, daß ein Programm absolut fehlerfrei und nicht weiter verbesserungswürdig sei so daß es ein Entwurfsmuster für gleichartige Fälle darstellen würde. Und für ähnliche Fälle schon gar nicht.

        Programmieren ist ein Handwerk und das heißt, daß es jeder selber lernen muß (Niklaus Wirth).

        MfG

        1. Tach!

          Ein Programmierer erfindet mit jedem Programm das Rad neu.

          Verwechselst du da gerade Erfinden mit Erstellen? Wenn sich ein Programm fortbewegen soll, muss der Programmierer das Rad nicht neu erfinden sondern lediglich eins oder mehrere erstellen. Dabei wäre es nicht sehr sinnvoll, das bewährte Muster "runde Scheibe mit Achse in der Mitte" zu ignorieren. Das Muster hindert den Programmierer jedenfalls nicht daran, das Rad individuell zu gestalten, so dass es den besonderen Ansprüchen der Anwendung gerecht wird.

          Ein Programmierer wird niemals mit Sicherheit sagen können, daß ein Programm absolut fehlerfrei und nicht weiter verbesserungswürdig sei so daß es ein Entwurfsmuster für gleichartige Fälle darstellen würde. Und für ähnliche Fälle schon gar nicht.

          Mir erschließt sich nicht, wo da der Zusammenhang sein soll. Nur weil man beim konkreten Ausführen Fehler machen kann, sind Vorschläge zu Vorgehensweisen unbrauchbar?

          dedlfix.

        2. moin pl,

          da bin ich anderer Meinung aber ich denke das ist auch nur ein dummes blödes Missverständnis und wir beide meinen das gleiche. Also nix für ungut 😉. Ich bin Ohnmächtig das Missverständnis aufzudrösel.

          vlg MB

          1. Deine Meinung ist ja Ok. Nur solltest Du diese sog. "Design Patterns" nicht überbewerten. Gerade das SRP ist ja, so wie es im Wiki beschrieben ist, absolut blödsinnig, es führt dazu dass ein Programm atomized wird. Eine Zerlegung in nicht mehr teilbare Funktionseinheiten erschwert extrem die Fehlersuche!

            In der Praxis jedoch versteht sich SRP genau umgekehrt, nämlich so wie im richtigen Leben: Wenn es nicht läuft wie erwartet, greift man sich denjenigen der die Verantwortung trägt und strebt für die Fehlersuche kurze Wege an. Sicher wird man nicht drumherum kommen mit mehreren Instanzen zu arbeiten aber man möchte nur eine einzige Instanz haben die mit jeder Funktion die im Verlauf eines Programmes aufgerufen wird somit seine eigene Methode aufruft: $this!

            So kann man jederzeit einen Dump werfen und gucken was an dem $this kaputt ist und hat so den Fehler in Kürze eingegrenzt, beispielsweise zeigt der Dump ein fehlendes oder undefiniertes Request-Objekt, was als eine Eigenschaft in $this eingebaut sein sollte (Aggregation). Solche und weitere Beispiele die sich beliebig fortsetzen lassen zeigen doch daß es keineswegs vorrangig ist, nach einem bestimmten Muster entwickeln zu wollen! Vielmehr ist es ungemein wichtiger seinen eigenen Stil und insbesondere eigene Fertigkeiten zu entwickeln und auszubauen.

            Wer seinen ganzen Urlaub damit verbringt, fliegenden Möven hinterherzujagen nur weil das Bild einer fliegenden Möve in der letzten Fotoausstellung den ersten Preis bekam, hat den Sinn der Fotografie nicht verstanden. Genauso ist es mit Programmieren. Wie Niklaus Wirth bereits in den späten 70ern feststellte, kann ein Dozent nur die Grundlagen vermitteln, das Programmieren an sich jedoch, muss jeder selber lernen.

            Design Patterns sind so gesehen mitnichten also Entwurfsmuster sondern allenfalls grundlegende Dinge. Sie zeichnen keinen Weg vor den man gehen kann sondern höchstens Wege die andere gegangen sind. Aber nicht der Weg an sich führt zum Ziel sondern die eigene Trittsicherheit! Und auch das Umkehren will gelernt sein, sprich: Die Fehlersuche.

            Schönen Sonntag!

            1. moin pl,

              Nur solltest Du diese sog. "Design Patterns" nicht überbewerten.

              Das werde ich auch nicht machen da sind wir D'accord ;-).

              Gerade das SRP ist ja, so wie es im Wiki beschrieben ist, absolut blödsinnig, es führt dazu dass ein Programm atomized wird. Eine Zerlegung in nicht mehr teilbare Funktionseinheiten erschwert extrem die Fehlersuche!

              Das stimmt aus meiner Erfahrung als Laie. Daher habe ich auch Try-Catch-Blöcke gemacht. Die Trennung ist für mich aber essentiell, also wenn ein der Fehler / Exception auftritt und er/sie mal nicht im Try-Catch-Block geschiet ich dort zusuchen habe wo sie entschtehen.

              So kann man jederzeit einen Dump werfen und gucken was an dem $this kaputt ist und hat so den Fehler in Kürze eingegrenzt

              Das ist gut. Das habe ich noch nich gemacht vielen Dank dafür. Werde ich beherzigen.

              Vielmehr ist es ungemein wichtiger seinen eigenen Stil und insbesondere eigene Fertigkeiten zu entwickeln und auszubauen.

              Werde ich immer tun. Ich will aber auch dass das ganze angepasst an andere Programmierer schreiben, damit sie meine Code verstehen und weiterentwickeln oder verändern können und sich nich auf den Kopf stellen müssen um den Code zu verstehen.

              kann ein Dozent nur die Grundlagen vermitteln, das Programmieren an sich jedoch, muss jeder selber lernen.

              Meine Rede. Theoretisches Wissen ist gut Praxis ist auch gut. Man muss IMO beides wissen und anwenden können.

              Und auch das Umkehren will gelernt sein, sprich: Die Fehlersuche.

              ganz meine Meinung, da sind wir wieder D'accord

              Schönen Sonntag!

              Ebenfalls

              vlg MB

              1. hi Lieber Kollege,

                Werde ich immer tun. Ich will aber auch dass das ganze angepasst an andere Programmierer schreiben, damit sie meine Code verstehen und weiterentwickeln oder verändern können und sich nich auf den Kopf stellen müssen um den Code zu verstehen.

                Ja, darauf kommt es an. Manchmal ists ja auch so, dass man nach einiger Zeit selbst der Andere ist. Also, ich hab da ein Programm, da läuft was nicht, die Seite wird in 'de' ausgegeben anstelle 'en'. Wen schnappe ich mir? Genau: $this! Tatsächlich habe ich viele Kollegen kennengelernt, die gucken als Erstes in den Konstruktor und werfen als Nächstes einen Dump um zu vergleichen ob das was der Konstruktor verspricht auch stimmt.

                Schön auch, wenn es keine globalen Variablen gibt und die allesamt an $this gewanzt sind -- So hat man alles gleich auf dem Schirm. Auch die Abhängigkeiten sind zu sehen, also Eigenschaften die ihrerseits wiederum Instanzen anderer Klassen sind. So sieht ein Neuankömmling bspw. eine Eigenschaft 'Config' [CFG] like this:

                My Object
                (
                    [CFG] => Config Object
                        (
                           [LANG] => de
                        )
                
                )
                

                und weiß damit sofort, dass es für [CFG] auch Methoden geben muss, um an die Config ranzukommen in lesender oder schreibender Weise. Im speziellen Fall sollte LANG nicht 'de' sein, also ab in den eigenen Konstruktor um zu gucken, was bei der Initialisierung schiefgegangen ist. Ggf. sind beim Setzen der LANG noch weitere Instanzen beteiligt, etwa ein Requestobjekt was wiederum Methoden mitbringt welche den Accept-Language-Header parsen.

                So ist es eine Frage der Zweckmäßigkeit, den Code aufzuteilen, insbesondere hinsichtlich Debugging und nicht etwa der Drang nach atomaren Funktionseinheiten. Debugging verlangt kurze Wege und wer einmal das Vergnügen hatte in der Hotline zu sitzen weiß wie ungehalten manche Kunden sein können ***grrr.

                Schönen Sonntag 😉

            2. Tach!

              Gerade das SRP ist ja, so wie es im Wiki beschrieben ist, absolut blödsinnig, es führt dazu dass ein Programm atomized wird. Eine Zerlegung in nicht mehr teilbare Funktionseinheiten erschwert extrem die Fehlersuche!

              Oder sie erleichtert sie extrem, weil diese kleinen Einheiten sehr einfach testbar sind, vor allem automatisiert.

              In der Praxis jedoch versteht sich SRP genau umgekehrt, nämlich so wie im richtigen Leben: Wenn es nicht läuft wie erwartet, greift man sich denjenigen der die Verantwortung trägt und strebt für die Fehlersuche kurze Wege an.

              Genau deshalb nimmt man kleine überschaubare Einheiten.

              So kann man jederzeit einen Dump werfen und gucken was an dem $this kaputt ist und hat so den Fehler in Kürze eingegrenzt, beispielsweise zeigt der Dump ein fehlendes oder undefiniertes Request-Objekt, was als eine Eigenschaft in $this eingebaut sein sollte (Aggregation).

              Dem steht SRP auch nicht im Wege.

              Wie Niklaus Wirth bereits in den späten 70ern feststellte, kann ein Dozent nur die Grundlagen vermitteln, das Programmieren an sich jedoch, muss jeder selber lernen.

              Hat er auch gesagt, dass dabei ausgeschlossen ist, sich Anleihen bei andern zu holen, sei es in Form von Mustern oder Prinzipien?

              Aber nicht der Weg an sich führt zum Ziel sondern die eigene Trittsicherheit! Und auch das Umkehren will gelernt sein, sprich: Die Fehlersuche.

              Inwiefern beeinflusst das Verwenden von Mustern und Prinzipien die Fehlersuche und die Fähigkeit dazu?

              dedlfix.

            3. Hallo pl,

              SRP ist kein GOF Design Pattern, sondern ein Grundprinzip der OOP. Allerdings sind die GOF-Patterns darauf ausgelegt, den Programmierer zum SRP hinzuführen, wenn ich das richtig verstehe.

              Witzig finde ich den Begriff "Raviolicode", den die Wikipedia für konsequent angewendetes SRP nennt, und den die englische Wikipedia als Antipattern bezeichnet.

              Die Lösung, die die deutsche Wikipedia anbietet, nämlich saubere Strukturierung, erlaubt es vermutlich, die Übersicht zu behalten - aber sich in ein Ravioliprojekt einzuarbeiten stelle ich mir deutlich härter vor als in ein Spaghetticodeprojekt. Bei Spaghetti kann ich den Nudeln wenigstens in Ruhe folgen. Die Ravioli flutschen mir dauernd durch die Finger und ich muss die Portion einfrieren (sprich: Per Debugger STOPP brüllen und dann die Callstacks und Objektreferenzen aufdröseln), um eine Ahnung von dem zu bekommen, was geschehen könnte.

              Was bleibt: Alles mit einer Prise Salz genießen, und vielleicht doch lieber Maultaschen statt Ravioli kochen.

              Rolf

              --
              Dosen sind silbern
              1. Hi,

                Dein Ravioli/Spaghettivergleich ist nicht schlecht 😉

                Auf jeden Fall verfolgt man mit der Aufteilung in Funktionseinheiten völlig andere Ziele als SRP definiert, nämlich diese hier:

                1. Übersichtlicher code
                2. wartbarer Code
                3. Teamarbeit
                4. Effizienz
                5. Debugging
                6. Vermeidung von Coderedundanzen
                7. Skalierbarkeit

                (ohne Anspruch auf Vollständigkeit)

                SRP ist kein GOF Design Pattern, sondern ein Grundprinzip der OOP.

                Natürlich kann kann OOP auch zum Selbstzweck betreiben, das ist jedoch der Praxis ferner denn je. Den Hauptgrund überhaupt OOP einzusetzen, fand ich bei Eric Foster Johnson in einem Buch über Perlmodule: Der Hauptvorteil von OOP besteht darin, daß sie besser mit Veränderungen umgehen kann (7). Und genau das hat sich in meiner Praxis täglich bis stündlich bewahrheitet.

                Ebensowenig zeigen Patterns der GOF Wege bzw. Vorgehensweisen die zielführend sind. Es sind vielmehr Muster die man sogar nebeneinander in Programmen vorfinden kann, ohne daß die Programmierer jemals die Absicht hatten, nach einem Muster der GOF vorgehen zu wollen. Beispiel Dependency Injection:

                Eine Instanz der Klasse Session wird dem Konstruktor der Responseklasse übergeben. Das macht man aber nicht weil man DI verwenden möchte, sondern einfach nur deswegen, weil die Sessioninstanz noch vor dem Erstellen des Response-Objekts vorliegen muss.

                Den Einbau der CGI Klasseninstanz hingegen kann man auch später vornehmen, also nachdem das Responseobjekt bereits vorliegt. Z.B. erst in dem Moment, wenn eine CGI-Methode aufgerufen wird. Und auch das ist keine Frage des Entwurfsmusters sondern eine Frage des Deployments und Softwareverteilung und der übrigen Punkte siehe obenstehend.

                Schöne Grüße.

                1. Tach!

                  Der Hauptvorteil von OOP besteht darin, daß sie besser mit Veränderungen umgehen kann.

                  Genau das ist das Ziel von SRP, dass Änderungen einfacher sind, weil eine Funktionseinheit nur genau einen Grund hat, sie ändern zu müssen.

                  Ebensowenig zeigen Patterns der GOF Wege bzw. Vorgehensweisen die zielführend sind. Es sind vielmehr Muster die man sogar nebeneinander in Programmen vorfinden kann, ohne daß die Programmierer jemals die Absicht hatten, nach einem Muster der GOF vorgehen zu wollen. Beispiel Dependency Injection:

                  Eine Instanz der Klasse Session wird dem Konstruktor der Responseklasse übergeben. Das macht man aber nicht weil man DI verwenden möchte, sondern einfach nur deswegen, weil die Sessioninstanz noch vor dem Erstellen des Response-Objekts vorliegen muss.

                  Widersprichst du dir gerade selbst? Nicht zielführend, dann aber genau das Muster verwenden, weil es im konkreten Fall zielführend ist?

                  Irgendwie habe ich den Eindruck, dass du gegen die Muster argumentierst, nur um dagegen zu sein und was eigenes nehmen zu können. Selbst wenn das am Ende genau dem entspricht, was da mal jemand generell beschrieben und ihm einen Namen gegeben hat.

                  Was hast du eigentlich damals im Mathematikunterricht gemacht? Die Formeln ignoriert, die bereits andere herausgefunden und benannt hatten, um sie nochmal selbst zu erfinden?

                  dedlfix.

                  1. Was hast du eigentlich damals im Mathematikunterricht gemacht? Die Formeln ignoriert, die bereits andere herausgefunden und benannt hatten, um sie nochmal selbst zu erfinden?

                    Mein Dozent hat irgendwann einmal gesagt: Schön dass die den Gausschen Algorithmus verwendet haben. Sie hätten diese Aufgabe jedoch viel einfacher lösen können 😉

                  2. Tach!

                    Der Hauptvorteil von OOP besteht darin, daß sie besser mit Veränderungen umgehen kann.

                    Genau das ist das Ziel von SRP, dass Änderungen einfacher sind, weil eine Funktionseinheit nur genau einen Grund hat, sie ändern zu müssen.

                    Was in der Konsequenz aber lange Wege zur Folge hat insbesondere hinsichtlich Debugging. Das Problem herauszufinden in welcher Datei eine bestimmte Methode definiert ist, zeigt sich umso mehr, je tiefer eine Klassenhierarchie ist. Sicher wird man bei der am weitesten unten liegenden Erweiterung die Suche beginnen aber was dem entgegenkommt ist ja von vornherein der Entwurf einer flachen Klassenhierarchie.

                    Insgesamt jedoch sehe ich aber auch darin keinen Widerspruch der nicht lösbar wäre. Richtig dumm wirds nur, wenn man Entwurfsmuster oder bestimmte Prinzipien zum Leitsatz erhebt, beispielsweise derart daß man strikt nach SRP entwickeln will. Perls tie() folgt scheinbar diesem Prinzip, so ruft jeder Zugriff auf ein Arrayelement eine Funktion auf, von der Wertzuweisung über den Iterator bis zur Herausgabe eines bestimmten Wertes ist alles in jeweiligen Funktionen definiert.

                    Der Grund für die Entwicklung von tie() ist jedoch nicht SRP an sich sondern ein ganz Anderer, nämlich das Binden von Variablen an Klassen. Von daher muß es eine für jeden Zugriff spezialisierte Methode geben weil man die Bindung gar nicht anders realisieren kann.

                    MfG

  4. Tach!

    Ein berufserfahrener Kommentator meine "funktionsaufrufe sind teuer". [...] Kommrt PHP mit diesem SRP klar oder sollte man das bei komplexen Systemen zugunsten der Perfomance mischen?

    Jeder Code braucht Zeit für seine Ausführung. Natürlich nimmt es mehr Zeit in Anspruch, mehr Code ausführen zu müssen, wenn Übergabe von Parametern, Übernahme von Ergebnissen und die Verwaltung des Funktionsaufrufs hinzukommt. Die Abwägungsfrage ist aber, ob du nicht lieber drei Euro fuffzich mehr für eine bessere Maschine ausgeben möchtest, die dieses Manko wieder ausgleicht, oder ob du viel Arbeitszeit in die Wartung eines unhandlichen, unübersichtlichen Code-Monsters investieren möchtest.

    Die meiste Ersparnis bekommst du nicht, indem du die Funktionsaufrufe minimierst, sondern indem du generell schaust, welche Arbeiten vermeidbar sind. Eine Seite (oder große Teile davon) muss zum Beispiel nicht jedes Mal neu erstellt werden, wenn sich deren Inhalt nicht ändert. Es bietet sich dann an, noch mehr Code in Form eines Caches auf das Problem zu werfen, wenn sich durch den einen Griff in den Cache eine aufwändige Neugenerierung vermeiden lässt. Der erste Aufrufer hat eine geringfügig längere Ausführungszeit als ein System ohne Cache, weil dieser ebenfalls noch bedient werden muss, alle anderen bekommen aber durch den Cache einen deutlichen zeitlichen Vorteil.

    dedlfix.

    1. moin,

      Die meiste Ersparnis bekommst du nicht, indem du die Funktionsaufrufe minimierst, sondern indem du generell schaust, welche Arbeiten vermeidbar sind. Eine Seite (oder große Teile davon) muss zum Beispiel nicht jedes Mal neu erstellt werden, wenn sich deren Inhalt nicht ändert. Es bietet sich dann an, noch mehr Code in Form eines Caches auf das Problem zu werfen, wenn sich durch den einen Griff in den Cache eine aufwändige Neugenerierung vermeiden lässt. Der erste Aufrufer hat eine geringfügig längere Ausführungszeit als ein System ohne Cache, weil dieser ebenfalls noch bedient werden muss, alle anderen bekommen aber durch den Cache einen deutlichen zeitlichen Vorteil.

      So isses. Der erste Aufrufer muss z.B. eine Methode bemühen um das Template für den Body ggf. aus dem Dateisystem zu holen. Beim nächsten Aufruf und allen Folgenden jedoch ist das Template direkt per RAM adressierbar. Lediglich die Werte die da reinzurendern sind können von Request zu Request unterschiedlich sein.

      Darüber hinaus bleibt ja auch der Code im Hauptspeicher und zwar kompiliert. Egal ob das Klassen sind oder einzelne Methoden, das Laden derer aus dem Dateisystem muss nur einmal erfolgen.

      Im Übrigen sind Design Patterns nicht das Backbone der Programmierung, nicht die Bohne sind sie das.

      MfG

      1. moin pl,

        Im Übrigen sind Design Patterns nicht das Backbone der Programmierung, nicht die Bohne sind sie das.

        Das sehe ich genau so.

        vlg MB

      2. Tach!

        Die meiste Ersparnis bekommst du nicht, indem du die Funktionsaufrufe minimierst, sondern indem du generell schaust, welche Arbeiten vermeidbar sind. Eine Seite (oder große Teile davon) muss zum Beispiel nicht jedes Mal neu erstellt werden, wenn sich deren Inhalt nicht ändert. Es bietet sich dann an, noch mehr Code in Form eines Caches auf das Problem zu werfen, wenn sich durch den einen Griff in den Cache eine aufwändige Neugenerierung vermeiden lässt. Der erste Aufrufer hat eine geringfügig längere Ausführungszeit als ein System ohne Cache, weil dieser ebenfalls noch bedient werden muss, alle anderen bekommen aber durch den Cache einen deutlichen zeitlichen Vorteil.

        So isses. Der erste Aufrufer muss z.B. eine Methode bemühen um das Template für den Body ggf. aus dem Dateisystem zu holen. Beim nächsten Aufruf und allen Folgenden jedoch ist das Template direkt per RAM adressierbar. Lediglich die Werte die da reinzurendern sind können von Request zu Request unterschiedlich sein.

        So meinte ich das nicht. Das Laden von der Festplatte ist bei häufig verwendeten Dateien bereits durch den Dateisystem-Cache des Betriebssystems entschärft. Das spart keine Ausführung von PHP-Code ein. Zudem arbeitet PHP nicht so, dass ein permanenter Teil stets geladen wäre, in dem man solche Dinge zwischenspeichern könnte. PHP muss also für jeden Request einen Dateisystemzugriff ausführen, wenn es ein Template laden möchte. Lediglich im Idealfall wird der durch den Dateisystem-Cache beschleunigt. Mein Vorschlag bezog sich auch nicht auf veränderliche Daten, sondern auf das was nicht ständig gerendert werden muss, weil es über längere Zeit für jeden Request gleich bleibt. Diese Arbeit soll das Cachesystem einsparen, weil das weniger Zeit verbraucht, als mit lediglich einer anderen Codestruktur eingespart werden kann. Bei beispielsweise einem CMS mit individueller Syntax, die vor dem Ausliefern erst nach HTML transformiert werden muss, ergibt sich oftmals keine Notwendigkeit, diesen Vorgang stets auszuführen, sondern es reicht, nur bei Bearbeitungsvorgängen eine neue Version in den Cache zu legen. In dem Fall fällt es nicht nur nicht ins Gewicht, wenn der Code theoretisch überflüssige Funktionsaufrufe enthält, die quasi nur der besseren Wartbarkeit dienen. Es ist auch im Ergebnis schneller als Code, der aus Optimierungsgründen auf die Separierung in Funktionen verzichtet.

        Darüber hinaus bleibt ja auch der Code im Hauptspeicher und zwar kompiliert. Egal ob das Klassen sind oder einzelne Methoden, das Laden derer aus dem Dateisystem muss nur einmal erfolgen.

        Auch das erspart keine Codeausführung. Diese Aussage ist nicht falsch, solange man PHP mit einen Opcode-Cache betreibt, geht aber am Thema komplett vorbei.

        Im Übrigen sind Design Patterns nicht das Backbone der Programmierung, nicht die Bohne sind sie das.

        Natürlich nicht. Sie sind nur Vorschläge, wie man Dinge auf bereits bewährte Weise lösen kann. Das darf man selbstverständlich ignorieren und seine eigenen Erfahrungen von Null beginnend sammeln.

        dedlfix.