Ankündigung

Einklappen
Keine Ankündigung bisher.

Class Chaining

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Class Chaining

    Das Method-Chaining
    PHP-Code:
    //Method-Chaining
    $result = (new Class1($param))
        ->
    options()
        ->
    set(..)

    dürfte allgemein bekannt sein. Doch was soll Class Chaining?
    Class Chaining verbindet Objekte ähnlich wie Kommandos welche durch eine Pipeline laufen.
    Dabei wird das Resultat aus einem Method-Chaining dem Konstruktor einer weiteren Klasse übergeben.
    Vom Grundsatz so:
    PHP-Code:
    result2 = (new Class1($param))
      ->
    options()
      ->
    set(..)
      ->
    toClass("Class2")  //Class-Chaining
      
    ->foo()
      ->
    bar()

    foo() und bar() sind Mehoden von Class2.

    Konkret ist damit so etwas möglich:
    PHP-Code:
    $data = [..];

    echo 
    tableArray::create($data)
        ->
    filterGroupSum('anzahl',["event_mat_id"])
        ->
    orderBy('anzahl DESC')
        ->
    toClass('Table')
        ->
    title(['Id','Anzahl'])

    Die Klasse tableArray verarbeitet ein 2-dimensionales Array. Das Ergebnis wird an eine Klasse Table weitergereicht, wo noch eine Titelzeile zugefügt wird und die dann das komplette HTML für eine Tabelle erzeugt.
    Das Resultat ist ähnlich wie
    Code:
    Id    Anzahl
    10116    30
    23       20
    10483    12
    10464    4
    Wollte dies einfach mal als Idee hier reinwerfen.

    LG jspit

  • #2
    Das Pattern kenne ich und die Probleme sind auch bekannt. z.B. dass foo() sowie bar() dann in jedem Editor einen Fehler werfen. Ich sehe es daher eher als Anti-Pattern.

    Besser finde ich daher die Umsetzung mit einer Chain Klasse.

    PHP-Code:
    $chain = (new Chain($input))
         ->
    run(Class1::class)
         ->
    run(Class2::class)
         ->
    getResult(); 
    oder:
    PHP-Code:
    $chain = (new Chain($input))
         ->
    run($class1)
         ->
    run($class2)
         ->
    getResult(); 

    Kommentar


    • #3
      Mir wäre lieber, wenn die Methode in der Class2 wäre und ein Objekt des Typen Class1 als Parameter annimmt.

      Auf das Beispiel bezogen könnte es dann neben der Klasse Table auch die Klasse CSV geben, die tableArray annimmt und entsprechend umwandelt.

      PHP-Code:

      echo new CSV (tableArray::create($data)
          ->
      filterGroupSum('anzahl',["event_mat_id"])
          ->
      orderBy('anzahl DESC')
          ->
      title(['Id','Anzahl'])); 
      [COLOR=#A9A9A9]Relax, you're doing fine.[/COLOR]
      [URL="http://php.net/"]RTFM[/URL] | [URL="http://php-de.github.io/"]php.de Wissenssammlung[/URL] | [URL="http://use-the-index-luke.com/de"]Datenbankindizes[/URL] | [URL="https://www.php.de/forum/webentwicklung/datenbanken/111631-bild-aus-datenbank-auslesen?p=1209079#post1209079"]Dateien in der DB?[/URL]

      Kommentar


      • #4
        Der Konstruktor von Class2 muss immer das Objekt von Class1 verarbeiten können. Dafür muss gesorgt werden.
        Obiger Code ist letztlich nur andere Notation für
        PHP-Code:
        $obj1 = (new Class1($param))
          ->
        options()
          ->
        set(..)
        ;
        $obj2 =  (new Class2($obj1))
          ->
        foo()
          ->
        bar()

        Und ja, genau so ist es gedacht, anstelle von Table auch Klassen wie deine angedeutete CSV nehmen zu können.

        PHP-Code:
        echo tableArray::create($data)
            ->
        filterGroupSum('anzahl',["event_mat_id"])
            ->
        orderBy('anzahl DESC')
            ->
        toClass('CSV')

        Deine Notation funktioniert genau so, nur finde ich eben die Notation mit toClass() übersichtlicher.

        Zum Verständnis noch der Code von der Methode toClass:
        PHP-Code:
        public function toClass($class){
          return new 
        $class($this);

        ---
        Zitat von Zeichen32 Beitrag anzeigen
        . dass foo() sowie bar() dann in jedem Editor einen Fehler werfen.
        Du meinst In deinem Editor, meine meckern da nicht.

        Kommentar


        • #5
          Zitat von jspit Beitrag anzeigen
          Du meinst In deinem Editor, meine meckern da nicht.
          Ich weiß nicht welchen Editor du verwendest, jeder einigermaßen gute sollte dort ein undefined method error werfen, da die statische Code-Analyse dort nicht greifen kann.
          Weiter nimmst du mit der Methode deiner "toClass" Klasse auch jede Möglichkeit weitere Abhängigkeiten zu haben. Was z.B. wenn die Klasse "csv" noch eine Lib für das Verarbeiten von CSV Dateien benötigt?

          Kommentar


          • #6
            So richtig verstehe ich den Sinn des ganzen nicht, gibt es dazu auch Infoer aus der grossen weiten welt des netzes ?
            Google findet mir das nichts, nicht mal den thread hier, auf den ersten 5 Seiten.

            Kommentar


            • #7
              Ging es vllt um das hier?

              https://www.php.de/forum/webentwickl...im-konstruktor
              [I]You know, my wife sometimes looks at me strangely. „Duncan“, she says, „there's more to life than Solaris“. Frankly, it's like she speaks another language. I mean, the words make sense individually, but put them together and it's complete nonsense.[/I]

              Kommentar


              • #8
                Zeichen32

                Ich benutze Visual Studio Code, aber ohne Erweiterungen für eine statische Codeanalyse.
                Das diese Analyse-Tools bei diversen dynamischen Anweisungen kapitulieren oder gar Fehler melden wo keine sind ist ja allgemein bekannt.
                Für mich ist so ein Editor oder eine IDE nur ein Werkzeug. Und von einem Werkzeug lasse ich mir nicht vorschreiben wie ich was zu programmieren habe.
                Habe Verständnis dafür, wenn auf solche Analysen im Vorfeld Wert gelegt wird.
                Ich für meine Anwendungen kann auf solche Codeanalysen verzichten, da praktisch nach jeder Änderung im Code ein Test unter realen Bedingungen gefahren wird wo sofort eventuelle Fehler sichtbar werden.

                Zur Methode toClass()
                Habe vergessen zu erwähnen das ich fürs Verständnis hier einen stark vereinfachten Code gezeigt habe.
                Es macht selbstverständlich Sinn die Möglichkeit zu haben der Folgeklasse noch weitere Parameter mitzugeben.
                Und nicht nur das. Es sollten auch sog. statische Konstruktoren ( Foo::create() ) mit Parametern bedient werden können.

                tomBuilder

                Habe bei der Suche nach einen passenden Titel auch nicht so richtig was gefunden. Dafür massenhaft Einträge für sog. Functions-Chaining.
                Ziel ist es mit einer verkürzten Schreibweise Aufgabenketten die von mehreren Klassen erledigt werden abzuarbeiten deren Zwischenobjekte nicht noch für andere Aufgaben benötigt werden.
                Ähnlich wie Pipes bei Linux: Input> Aufgabe1 | Aufgabe2 | Aufgabe3 >Output.

                Am Rande: Die letzten Einträge hier aus dem Forum die ich bei Google finden kann sind 5 Tage alt.

                chorn
                Tangiert das Problem nur am Rande. Die komplette Methode toClass() wie oben angedeutet existiert bereits.

                LG jspit

                Kommentar


                • #9
                  Ich habe mal in einem Buch einen sehr schlauen Satz gelesen: "Magic is bad, because it leads to wtf moments."

                  Und genau das ist das Probleme was ich angemerkt habe.
                  Natürlich ist es okay wenn man für sich alleine solche Anti-Pattern verwendet. Problematisch wird es immer dann, wenn auch andere mit dem Code zutun haben. Ich erinnere da gerne mal an die alten Zeiten von den ersten CodeIgniter oder CakePHP Versionen.
                  Dort war so etwas total üblich und hat auch richtig Spaß gemacht zu verwenden. Bis zu dem Zeitpunkt, wo etwas nicht mehr so funktioniert hat wie gedacht und man versuchen musste den Code zu verstehen.
                  Denn mit so viel Magie, ist das Debuggen die reinste Qual.

                  Darauf hin haben dann schlaue Leute das Problem erkannt und z.B. genau für dein vorgestelltes Anti-Pattern ein Pattern entworfen, welches das selbe macht aber deutlich einfacher zu verstehen und zu debuggen ist.
                  Dies nennt sich "Pipleline-Pattern" und habe ich oben in meinem Code-Beispiel kurz gezeigt.
                  https://medium.com/@aaronweatherall/...t-9b5f43a98130

                  Auch das du dazu im Internet nicht viel gefunden hast, sollte ein Indiz sein, dass es eher nicht so häufig verwendet wird und es sicher Gründe dafür gibt.

                  Kommentar


                  • #10
                    Zitat von jspit Beitrag anzeigen
                    tomBuilder

                    Habe bei der Suche nach einen passenden Titel auch nicht so richtig was gefunden. Dafür massenhaft Einträge für sog. Functions-Chaining.
                    Ziel ist es mit einer verkürzten Schreibweise Aufgabenketten die von mehreren Klassen erledigt werden abzuarbeiten deren Zwischenobjekte nicht noch für andere Aufgaben benötigt werden.
                    Ähnlich wie Pipes bei Linux: Input> Aufgabe1 | Aufgabe2 | Aufgabe3 >Output.

                    Am Rande: Die letzten Einträge hier aus dem Forum die ich bei Google finden kann sind 5 Tage alt.

                    LG jspit
                    Früher wurden die Einträge hier minütlich aktualisiert (

                    Ich schwimme immernoch zwischen method chaining bei dem eine andere classe returned wird und injection hin und her.
                    ich denke heuete abend mal in ruhe drüber nach...

                    Kommentar


                    • #11
                      Bsp.:
                      PHP-Code:
                      $chain = (new Chain($input))
                           ->
                      run(Class1::class)
                           ->
                      run(Class2::class)
                           ->
                      getResult(); 
                      Mal außer Acht gelassen, dass das so vermutlich nur in wenigen Fällen funktioniert, weil Class1 und Class2 auch meist Abhängigkeiten haben und unter der Prämisse, dass man Objekte vielleicht wieder verwenden möchte, würde es doch mehr Sinn machen, das Ganze so zu strukturieren:
                      PHP-Code:
                      $chain = new Chain([new Class1(), new Class2()]);
                      $result $chain->run($input); 
                      Damit könnte man Chain problemlos auch per Dependency Injection konfigurieren und immer wieder verwenden. Ich kannte dieses Pattern bis dato gar nicht. Das sieht mir aber auch etwas... murksig aus. Aber vielleicht könnt ihr mir die Vorteile erläutern, mich würden konkrete Use-Cases interessieren. Das sieht eher aus, als würde man versuchen, funktionale Programmierung in OOP zu pressen.

                      Kommentar


                      • #12
                        Zitat von xm22 Beitrag anzeigen
                        Bsp.:
                        Mal außer Acht gelassen, dass das so vermutlich nur in wenigen Fällen funktioniert, weil Class1 und Class2 auch meist Abhängigkeiten haben und unter der Prämisse, dass man Objekte vielleicht wieder verwenden möchte, würde es doch mehr Sinn machen.
                        Wahrscheinlich hätte ich mehr Informationen dazu schreiben sollen.
                        So sollte ausgesagt werden, dass der Klassennamen übergeben wird, und die Chain dann eine statische Methode in dieser Klasse aufruft.

                        Kommentar


                        • #13
                          Zitat von xm22 Beitrag anzeigen
                          Bsp.:
                          Ich kannte dieses Pattern bis dato gar nicht. Das sieht mir aber auch etwas... murksig aus. Aber vielleicht könnt ihr mir die Vorteile erläutern, mich würden konkrete Use-Cases interessieren. Das sieht eher aus, als würde man versuchen, funktionale Programmierung in OOP zu pressen.
                          Falls du das Pipeline-Pattern meinst, dann schau dir den Link von mir oben an dort wird es ganz gut erklärt. In Java gibt es dies z.B. nativ um Streams durch verschiedene Filter (die Pipeline) zu jagen. In JavaScript kennt man dieses Pipeline Pattern auch unter dem Namen "Observables", welche es erlauben eingehende Events durch eine reihe von Filtern (die Pipeline) zu schicken.

                          Kommentar


                          • #14
                            Das Pipeling an sich will ich nicht in Frage stellen - Darum ging es mir nicht. Und wie es prinzipiell implementiert ist, auch nicht - Nur, dass da Klassen statisch eingesetzt werden können, ist aus meiner sicht zu kurz gegriffen. Am Ende läuft es ja auf map/filter/reduce hinaus.

                            Kommentar


                            • #15
                              Class-Chaining ermöglicht innerhalb des Method-Chainings einer Klasse das Method-Chaining mit einem neuen Klassenobjekt fortzusetzen oder Methoden fremder Klassen mit einzubinden.
                              Dies wird durch das Einbinden einer einzigen Methode erreicht, die auch als Trait implementiert werden kann.
                              Die vorgestellten Alternativen durch eine übergeordnete "Wrapperklasse" welche sozusagen mehrere Klassen verwaltet ähnliches zu erreichen überzeugt mich nicht.
                              Dies hier ist auch erstmal nur eine Idee. Die Praxis wird zeigen, ob die Idee was taugt.

                              Eine Möglichkeit der Implementierung zeigt die folgende Methode toClass:
                              PHP-Code:
                               /*
                                * use or create a new class which continues method chaining
                                * @param string className
                                *   or array('class','create-method']
                                *   or array($prevClass,'method')
                                * @param ...args optional parameters
                                */  
                                
                              public function toClass(/*'className', ...args */){
                                  
                              $args func_get_args();
                                  
                              $class array_shift($args);
                                  if(
                              is_string($class)){
                                    return new 
                              $class($this,...$args);
                                  }
                                  elseif(
                              is_array($class)) {
                                    
                              $args array_merge([$this],$args);
                                    return 
                              call_user_func_array($class,$args);
                                  }
                                  else {
                                    throw new \
                              InvalidArgumentException('expect Class as first Parameter');
                                  }
                                } 
                              Diese Variante ermöglicht der folgenden Methode neben dem notwendigen Objekt als ersten Parameter noch weitere optionale Parameter mitzugeben.

                              Die Methode in Klassen aufzunehmen wo es sich anbietet ist kein Aufwand.

                              Die Nutzungsmöglichkeiten von toClass() sind vielseitig. Folgende Beispiele deuten dies an:
                              PHP-Code:
                              //Konventionell                            Chaining

                              $follow = new followClass($prev);          ->toClass('followClass')
                              $follow = new followClass($prev$par);    ->toClass('followClass'$par)
                              $follow followClass::create();           ->toClass(['followClass','create'])
                              $follow followClass::create($par);       ->toClass(['followClass','create'], $par)

                              $foo = new foo;
                              $bar = new bar;
                              $obj foo->method($bar);                  ->toClass([$bar,'method']) 
                              Beim lezten Beispiel wird vom return aus method bestimmt, mit welchen Objekt (foo, bar, other) ein Method-Chaining fortgesetzt werden kann.

                              Betreff Debugging haben Tests gezeigt das die Verwendung von toClass() nicht mehr Probleme macht als die durch das Method-Chaining ohnehin schon vorhanden sind.
                              Vom Grundsatz gilt, je mehr vom Method-Chainig Gebrauch gemacht wird und je kürzer die Notation ist, je schwieriger gestaltet sich ein Debuggen.
                              Einsteigern ist nur anzuraten solche Methoden wie toClass() nicht zu nutzen, sondern eine Notation nahe der konventionellen Schreibweise zu wählen.

                              Kommentar

                              Lädt...
                              X