Ankündigung

Einklappen
Keine Ankündigung bisher.

Class Chaining

Einklappen

Neue Werbung 2019

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

  • #16
    Zitat von jspit Beitrag anzeigen
    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.
    Sorry, aber das ist trotzdem ein Anti-Pattern.

    Du schreibst ja selber dass du keine statische Codeanalyse verwendest.
    Klar dass du dann beim Method-Chaining keine Unterstützung erhältst und das Debugging schwer fällt.

    Auch sehe ich überhaupt keinen praktischen Nutzen für dieses Pattern.
    Also für das Pipelining schon, aber nicht dass ich ein Objekt in ein anderes umwandeln möchte. Ich denke dann sind da schon ganz andere Probleme in der Architektur.
    Im Falle der CSV ist ja bspw. das viel lesbarer, logischer und lässt sich mit Type Hinting etc. vereinbaren:

    PHP-Code:
    $table = new Table(); // Table implements TableInterface

    $csv = new CSV();
    $csv->load($table); // expects TableInterface

    $csv = new Excel();
    $csv->load($table); // expects TableInterface 
    Nicht getestet, aber ich vermute auch mit Namespaces funktioniert das nicht einwandfrei?

    PHP-Code:
    use Foo\Bar\FooBar;

    $obj = new FooBar(); // funktioniert?
    $obj->toClass('FooBar'); // funktioniert nicht?
    $obj->toClass('Foo\Bar\FooBar'); // funktioniert? 

    Kommentar


    • #17
      Zitat von sboesch Beitrag anzeigen
      Du schreibst ja selber dass du keine statische Codeanalyse verwendest.
      Klar dass du dann beim Method-Chaining keine Unterstützung erhältst und das Debugging schwer fällt.
      Da hast du was fasch verstanden.
      Das Argument, das durch die Methode eine statische Codeanalyse erschwert wird, kam von Zeichen32.
      Richtig ist, ich brauche und verwende keine statische Codeanalyse im Editor. Kann mich also dazu nicht weiter äußern.

      Und ich habe sehr wohl eine gute Unterstützung auch fürs Method(Class)-Chaining und keinerlei Probleme beim Debuggen.
      ( Debugge jedoch nicht im Editor, sondern grundsätzlich über den Browser )

      Zitat von sboesch Beitrag anzeigen
      Auch sehe ich überhaupt keinen praktischen Nutzen für dieses Pattern.
      Also für das Pipelining schon, aber nicht dass ich ein Objekt in ein anderes umwandeln möchte. Ich denke dann sind da schon ganz andere Probleme in der Architektur.
      Nun das wird sich zeigen ob die Idee auch einen praktischen Nutzen bringt. Wenn sich da ein Anwendungsfall ergibt werden ich ihn den hier posten.

      Eine Objektumwandlung im Method-Chaining, also wo "unterwegs" andere Objekte benutzt werden, ist doch gängige Programmierpraxis.
      Nehmen wir nur mal PDO:
      PHP-Code:
      $result $pdo  //Object vom Typ PDO
        
      ->query($sql)  //Object vom Typ PDOStatement
        
      ->fetchAll()



      Zitat von sboesch Beitrag anzeigen

      Nicht getestet, aber ich vermute auch mit Namespaces funktioniert das nicht einwandfrei?

      [PHP ]
      use Foo\Bar\FooBar;

      $obj = new FooBar(); // funktioniert?
      $obj->toClass('FooBar'); // funktioniert nicht?
      $obj->toClass('Foo\Bar\FooBar'); // funktioniert?
      [/PHP ]
      Auch mit Namespaces sehe ich keine Probleme (-> Test).
      Klar ist doch auch, wo ein String für eine Klasse erwartet wird, kann ein Alias per use Foo\Bar\FooBar nicht greifen.
      Dort muss der volle Namespace benutzt werden (Deine Variante 3).




      Kommentar


      • #18
        $result = $pdo //Object vom Typ PDO
        ->query($sql) //Object vom Typ PDOStatement
        ->fetchAll()
        ;
        Das ist aber etwas anderes, als das was du mit deiner toClass() Methode machst.
        Hier gibt die query() Methode immer ein PDOStatement Objekt zurück.
        PHP-Code:
        public PDO::query string $statement ) : PDOStatement 
        Eine statische Code-Analyse ist dort also ohne Probleme möglich.

        Kommentar


        • #19
          Zitat von sboesch Beitrag anzeigen
          Nicht getestet, aber ich vermute auch mit Namespaces funktioniert das nicht einwandfrei?

          PHP-Code:
          use Foo\Bar\FooBar;

          $obj = new FooBar(); // funktioniert?
          $obj->toClass('FooBar'); // funktioniert nicht?
          $obj->toClass('Foo\Bar\FooBar'); // funktioniert? 
          Dann verwende keine Strings, sondern die ::class "Konstante".

          $obj->toClass(FooBar::class);,
          $obj->toClass(\Foo\Bar\FooBar::class);

          Auch die statische Code-Analyse ist zumindestens mit PHPStorm kein Problem. Mit .phpstrom.meta.php kann das problemlos abgebildet werden.

          Ich sehe hier eher Probleme in
          1. Verständlichkeit (das muss man erstmal wissen)
          2. bei fluent interfaces, die über mehrere Klasse gehen, ist nicht mehr ersichtlich, von was die Methoden aufgerufen werden
          3. eine (generische) Factory wo sie definitiv nix zu suchen hat (nogo für mich)
          4. ein Konstruktor eignet sich nicht als Interface

          Um 3. zu vermeiden, müsste mit Instanzen gearbeitet werden, was wiederum das Konzept ad absurdum führt.


          Kommentar


          • #20
            Zitat von erc Beitrag anzeigen

            Dann verwende keine Strings, sondern die ::class "Konstante".

            $obj->toClass(FooBar::class);,
            $obj->toClass(\Foo\Bar\FooBar::class);
            Stimmt.
            Aber eigentlich wollte ich auch auf fehlendes Type Hinting hinaus, dass ich nicht direkt Feedback erhalte, ob der übergebene Parameter gültig ist.
            Ich weiß nur, ich muss einen FQN als String übergeben. Dass diese Klasse allerdings ein bestimmtes Interface implementieren muss, werde ich erst während dem Testlauf feststellen.

            Zitat von erc Beitrag anzeigen
            Auch die statische Code-Analyse ist zumindestens mit PHPStorm kein Problem. Mit .phpstrom.meta.php kann das problemlos abgebildet werden.
            Warum einfach, wenn es auch kompliziert geht.
            Und wenn dann noch jeder PhpStorm nutzt, war die Arbeit fast nicht umsonst...

            Kommentar


            • #21
              Ich bin bei diesem Konstrukt immer noch skeptisch:

              1. Wenn es eh nur statische Methoden sind, die beim Chaining aufgerufen werden, warum dann nicht gleich Methoden statt Klassen übergeben? Wenn es partout Klassen sein müssen: Stell Dir mal vor, diese Klasse soll sämtlichen Einträgen einen Wert aus der Datenbank hinzufügen. Dazu ist eine Abhängigkeit notwendig, nämlich die DB-Verbindung. Wie willst Du die sauber in Deine statische Methode hineinbekommen?

              2. Hat es einen bestimmten Grund, dass Du toClass explizit einen String und kein ...::class übergibst?

              3. Das toClass hat eine eingebaute Schwäche, die Du im umgekehrten Fall nicht hättest: Du kannst dort _jede_ Klasse hineingeben und es würde nicht mal einen Fehler geben, wenn der Konstruktor keine Argumente entgegen nehmen würde. Im schlimmsten Fall würdest Du eine Exception bekommen, aber auch nur, wenn Du etwas hinein wirfst, was absolut nicht verwertbar ist. Das ist weit weg von sauber und explizit.

              4. Die Argumente an toClass zu übergeben ist in meinen Augen nur ein Workaround um auf Biegen und Brechen diese toClass-Methode benutzen zu können. Das macht es sogar unmöglich, die Argumente zu type-hinten.

              Zusammengefasst: Ich will das überhaupt nicht schlecht reden und selbstverständlich lässt sich das anwenden. Es ist nur schon von vornherein konzeptionell fragwürdig.

              Kommentar


              • #22
                Wenn mit Namespaces gearbeitet wird (die ich selbst wenig nutze) ist es ein guter Hinweis mit der Konstante ::class anstelle von Strings zu arbeiten. Angedeutet:
                PHP-Code:
                use nstest\followClass2;

                // :
                  
                ->toClass(followClass2::class); 
                Die Verständlichkeit, also was passiert beim Aufruf von toClass() in Verbindung mit den notwendigen ersten Parameter für die Vorgängerklasse im Konstruktor der Folgeklasse ist ein echtes Problem.

                Argumente wie
                eine (generische) Factory wo sie definitiv nix zu suchen hat (nogo für mich)
                resultieren aus speziellen Erfahrungen des Verfassers.Da versuche ich erst gar nicht diese zu entkräften.

                Mit Instanzen anstelle von ::class kann jedoch auch gearbeitet werden. Die Klasse zu der mit toClass() dann gewechselt wird muss jedoch mindestens eine Methode haben, welche die Instanz der Vorgängerklasse entgegennimmt.
                Habe das nur kurz angestestet (s.a. diesen test bei Line 112) und noch keinen Überblick welche (verrückten) Möglichkeiten sich hieraus noch ergeben könnten.

                Zum Thema Type-Hinting

                Dies muss wenn dies gewünscht wird nach meinem Verständnis doch im Konstruktor der Klasse realisiert werden zu der gewechselt wird.
                PHP-Code:
                    public function __construct(iprevClass $prevClassidiobj $obj){.. 
                Die Methode toClass() hat doch damit nichts zu schaffen. Komlett-Beispiel dazu hier in der Sandbox.

                ---
                Wenn es eh nur statische Methoden sind, die beim Chaining aufgerufen werden,
                Verstehe ich nicht. Method-Chaining funktioniert doch gar nicht mit statischen Methoden. Die einzigen statischen Methoden die in den Bespielen gezeigt wurden sind statische Konstruktoren (create) welche eine neue Instanz beim "Klassenwechsel" liefern.

                Zum Argument das toClass() jede Klasse ohne zu meckern entgegennimmt, auch solche, die im Konstruktor keine Argumente haben.
                Ja, das ist so. Man muss schon wissen was man da tut und die Methoden welche dann per Method-Chaining aufgerufen werden müssen auch auf mit dem Objekt der Vorgängerklasse umgehen können.
                Das bei überschüssigen Argumenten (zur Laufzeit) nicht gemeckert wird ist ja auch bei einer konventionellen Herangehesweise der Fall:
                PHP-Code:
                class test{
                  public function 
                __constrct(){  //ohne Argumente
                  
                }
                }

                $test = new test("parameter"); 
                Um dies in der Methode toClass() auch noch abzufangen müsste man die ReflectionClass zu Hilfe ziehen.

                Kommentar


                • #23
                  Wenn mit Namespaces gearbeitet wird (die ich selbst wenig nutze)
                  Generell: Wieso nicht?

                  Dies muss wenn dies gewünscht wird nach meinem Verständnis doch im Konstruktor der Klasse realisiert werden zu der gewechselt wird.
                  Das ist richtig. Doch damit erzeugst Du ein implizites Verhalten, da Du die Instanziierung eine Ebene nach unten verschiebst. Wenn Du normal den Konstruktor der Klasse benutzt, zu der gewechselt, hast Du eine größere Explizitität. Das ist allerdings in der Tat etwas abstrakter und in der reinen Benutzung nicht so extrem relevant.

                  Es gibt aber ein weiteres Problem: Du hast nach außen hin keine Information darüber, was Du zurückbekommst. Das bedeutet überspitzt gesagt, dass Du Dich ab dem Aufruf von toClass im Blindflug bewegst. Es würde gerade noch Sinn machen, wenn Du als Rückgabewert von toClass ein Interface hast.

                  Verstehe ich nicht. Method-Chaining funktioniert doch gar nicht mit statischen Methoden.
                  Da lag ich falsch. Ich bin im Post verrutscht und hatte den von Zeichen32 gesehen.

                  Man muss schon wissen was man da tut
                  Naja - Ich sage Dir aus 12 Jahren Entwicklungserfahrung, dass das _kein_ Argument ist

                  Vielleicht bin ich da zu extrem, aber es ist einfach zu "magic" und beruht zu sehr auf Annahmen. Und ich weiß auch, dass etliche Libraries und sogar built-in Funktionen von php das so handhaben. Daher - Meine Meinung. Und die muss nicht notwendigerweise der Wahrheit letzter Schluss sein

                  Dies ist übrigens auch einer der Gründe, warum ich und immer mehr zu Go wechseln.

                  Kommentar


                  • #24
                    Wir schweifen hier etwas ab. Aber zum Thema ist alles gesagt denke ich.
                    Zitat von xm22 Beitrag anzeigen
                    Generell: Wieso nicht?
                    Nutze Namespaces wenig, da​​​​​​
                    - meine "Zielplattformen" sehr wenig Speicher und Performance haben
                    - die Applikationen dafür alles Ein-Mann-Projekte sind
                    - nahezu 100% eigener Code
                    - ich auch kein Composer, keine Frameworks oder deren Komponenten nutze
                    Um nur einige Gründe zu nennen.

                    Kurz: Sehe für diese (meine) Projekte keine Notwendigkeit und keine Vorteile Namespaces zu nutzen.

                    Zitat von jspit
                    Man muss schon wissen was man da tut
                    Zitat von xm22 Beitrag anzeigen
                    Naja - Ich sage Dir aus 12 Jahren Entwicklungserfahrung, dass das _kein_ Argument ist
                    Ich fürchte da muss ich dir zustimmen. Heutzutage entwickeln immer mehr Leute die nicht wissen was sie tun.
                    So ein alter Trottel wie ich der noch Ferritkernspeicher aus eigener Praxis kennt hängt eben immer noch an alten Idealen.



                    Kommentar


                    • #25
                      Ferritkernspeicher
                      _Das_ ist in der Tat lange her

                      Kommentar


                      • #26
                        jspit Frage, da Du ja auf Systemen mit wenig Ressourcen arbeitest: Warum nimmst Du dann nicht was anderes - Z. B. eben Golang. Dann brauchst Du weder Webserver noch FPM o.ä...

                        Kommentar


                        • #27
                          Go bietet zweifellos einige Vorteile gegenüber PHP. Bei der Entwicklung für meine Anwendungen mit PHP muss ich eben einige Randbedingungen beachten. Damit kann ich leben.
                          K.o.-Kriterium für Go ist der Entwickler der Sprache und das Land wo diese Firma ihren Hauptsitz hat. Beide sehe ich nicht als sichere Basis für eine zukünftige Entwicklung.

                          Kommentar


                          • #28
                            Zitat von jspit Beitrag anzeigen
                            (..)
                            K.o.-Kriterium für Go ist der Entwickler der Sprache und das Land wo diese Firma ihren Hauptsitz hat. Beide sehe ich nicht als sichere Basis für eine zukünftige Entwicklung.
                            ????
                            offtopoic:
                            Pike und Thomson sind wohl die bedeutenstensten Namen, welche heutzutage hinter einer Sprache stehen können.

                            Google und Amerika komplett ausklammern zu wollen ist sicher edel.
                            Google ist für ein solches Ansinnen in meinen Augen einfach zu weit und wesentlich zu tief in dem was wir so IT nennen verankert.

                            Kommentar


                            • #29
                              jspit Wegen Google als solchem oder was ist Deine Befürchtung?

                              Aber selbst unter dieser Prämisse wären doch andere Sprachen besser geeignet.

                              Kommentar


                              • #30
                                Hier noch ein Beispiel. Es wird eine CSV eingelesen (Hier vom String um die Daten zu sehen), dort mit der Klasse tableArray 2 Spalten ausgewählt, Formatiert und das ganze sortiert.
                                Das letzte Kettenglied ist die Klasse Table, welches HTML erzeugt.
                                PHP-Code:
                                //Daten, normalerweise in einer Datei
                                $csvStr '1;"test11";3.4
                                2;"test2";23.9
                                3;"test7";45.1234'
                                ;

                                //Class-Chaining
                                $chain csvFileObject::createFromString($csvStr)
                                  ->
                                toClass('tableArray')

                                    
                                //class tableArray
                                  
                                ->select('1 as name, FORMAT("%6.2f",2) as value')
                                  ->
                                orderBy('name NATURAL')
                                  ->
                                toClass('Table')

                                    
                                //class Table
                                  
                                ->Title(['Name','Value'])
                                ;

                                //Ausgabe
                                echo $chain
                                Ausgabe:
                                Name Value
                                test2 23.90
                                test7 45.12
                                test11 3.40
                                .
                                Class-Chaining so wie hier vorgestellt geht nur für "gleichartige" Objekte (Hier im Grunde Tabellen), was die Zahl der sinnvollen Anwendungen stark einschränkt.

                                Kommentar

                                Lädt...
                                X