.MB: Ducktype

moin community,

zu Ducktype: man hat mir versucht zu erklärt was ducktype ist und mir den Spruch gesagt. Ich hab jetzt ne Ahnung aber keine Gewissheit. Was sind Ducktypes? Anwendungsbeispiele mit Metasyntaktischen Variablen + Scourcecode Doku ist extrem hilfreich.

vlg MB

  1. Tach!

    Was sind Ducktypes?

    Das ist kein Ding, das ist ein Konzept. Das kann man nur in schwach typisierten Sprachen verwenden, in anderen braucht es Interfaces oder Vergleichbares.

    "Wenn es quakt wie eine Ente, dann wird es wohl eine Ente sein", heißt der Spruch. Das beeinhaltet, dass es auch ein Fuchs sein kann, der wie eine Ente quakt. Das ist dem Programm an der Stelle aber egal, es will nur die Funktionalität des Quakens haben.

    Angenommen es geht um Javascript und der Anwendungsfall ist, dass man eine Speicherroutine schreibt, die Objekte entgegennimmt, um deren Daten zu speichern. Die Objekte müssen dabei kooperieren, indem sie eine Methode namens serialize() haben, die die Daten in eine String-Form bringen muss. Man testet nun, ob ein übergebenes Objekt die Methode serialize() hat, und wenn es die hat, dann ruft man sie auf. Wenn nicht, ignoriert man das Objekt oder wirft Fehler oder was auch immer man grad für angebracht hält. Der Speicherroutine ist also egal, was da kommt, Hauptsache es lässt sich serialisieren.

    Wenn also das Objekt eine Methode serialize() hat, ist es serialisierbar, wenn nicht, dann nicht. In C# müsste stattdessen die Klasse das Interface ISerializable implementieren, welches die Methode serializable() vereinbart. Oder es müsste eine Basisklasse geben, von der alle Kandidaten abgeleitet werden. Letzeres ist nciht unbedingt wünschenswert, besonders wenn die Klassen funktional nichts miteinander zu tun haben. Die Speicher-Funktion nähme jedenfalls ein Objekt vom Typ ISerializable oder von der Basisklasse entgegen. Dass das eingehalten wird, dafür sorgt der Compiler mit seiner Typprüfung. In Javascript gibt es keine Typangaben und so kann kein Compiler dafür sorgen, dass ein Fehler geworfen wird. Man muss dort selbst prüfen, ob man was passendes bekommen hat, sprich: irgendwas, das wie eine Ente quakt.

    dedlfix.

    1. du meinst also sowas in der art ? interface id und dann über die classen Foo.id und Bar.id ?

      wo kommt ein Ducktype zum tragen?

      interface Foobar {
        fuz();
        baz();
      }
      
      class Foo implements Foobar{
        public void fuz() {}
        public void baz() {}
      }
      
      1. Tach!

        du meinst also sowas in der art ? interface id und dann über die classen Foo.id und Bar.id ?

        wo kommt ein Ducktype zum tragen?

        interface Foobar {
          fuz();
          baz();
        }
        
        class Foo implements Foobar{
          public void fuz() {}
          public void baz() {}
        }
        

        In diesem Beispiel nicht. Beim Duck typing kommt es nicht darauf an, ob eine Instanz einer Klasse angehört oder ein Interface implementiert, sondern ob sie eine bestimmte Eigenschaft oder Methode hat. Nebenan hab ich mal Duck typing und andere Vorgehensweisen vergleichend dargestellt. Und die Wikipedia hat eine Seite mit vielen Codebeispielen in unterschiedlichen Sprachen.

        dedlfix.

    2. Tach!

      "Wenn es quakt wie eine Ente, dann wird es wohl eine Ente sein", heißt der Spruch. Das beeinhaltet, dass es auch ein Fuchs sein kann, der wie eine Ente quakt. Das ist dem Programm an der Stelle aber egal, es will nur die Funktionalität des Quakens haben.

      Konkretes Beispiel für PHP: Die Aufgabenstellung ist, sicherzustellen dass man ein passendes Objekt bekommt.

      Methode 1: Type Hinting. Der Parser/Compiler schreit, wenn es nicht passt. Alles was übergeben wird, muss von diesem Typ sein (statt Klasse geht auch Interface).

      class Duck {
        function quack();
      }
      
      function foo(Duck $duck) {
        $duck->quack();
      }
      
      foo(new Duck()); // quakt
      foo(new Person()); // Fehlermeldung
      

      Methode 2: einfacher Parameter ohne Typangabe, Prüfung zur Laufzeit im Code.

      function foo($duck) {
        if ($duck instanceof Duck) {
          $duck->quack();
        }
      }
      
      foo(new Duck()); // quakt
      foo(new Person()); // quakt nicht
      

      Methode 3: Duck typing. Hier mit Prüfung. Man kann die auch weglassen, dann gibt es eben einen Laufzeitfehler, wenn die Methode nichtg existiert.

      function foo($duck) {
        if (method_exists($duck, 'quack') {
          $duck->quack();
        }
      }
      
      class Duck {
        function quack() {...}
      }
      
      class Person {
        function quack() { play_duck_sound(); }
      }
      
      class Tree {
      }
      
      foo(new Duck()); // quakt
      foo(new Person()); // quakt auch
      foo(new Tree()); // quakt nicht, kann es nicht und auch nicht nachmachen
      

      dedlfix.

      1. Konkretes Beispiel für PHP: Die Aufgabenstellung ist, sicherzustellen dass man ein passendes Objekt bekommt.

        Erstmal herzlichen Dank das du meine Bitte aufgegriffen hast. Die ersten zwei Methoden habe ich verstanden. Das kam bei mir auch zum Einsatz, wusste aber nicht, dass das so heißt. Die dritte Methode checke ich nich. Alle Klassen Person, Duck und Tree referenzieren doch nicht mit der Funktion foo außerhalb der Klasse? Aber die Theory dahinter hjabe ich gecheckt. Danke Dir :)

        vlg MB

        1. Tach!

          Erstmal herzlichen Dank das du meine Bitte aufgegriffen hast. Die ersten zwei Methoden habe ich verstanden. Das kam bei mir auch zum Einsatz, wusste aber nicht, dass das so heißt.

          Die ersten beiden Methoden sind kein Duck Typing, sondern sie orientieren sich am konkreten Typ und nicht an einer bestimmten Verhaltensweise.

          Die dritte Methode checke ich nich. Alle Klassen Person, Duck und Tree referenzieren doch nicht mit der Funktion foo außerhalb der Klasse? Aber die Theory dahinter hjabe ich gecheckt. Danke Dir :)

          Es gibt keinen Zusammenhang zwischen den drei Klassen in Form eines Interfaces oder eines gemeinsamen Basistyps. Trotzdem kann man zumindest die Ente und die Person in der Funktion foo() verwenden, weil sie sich in dem einen Punkt, auf den es ankommt, gleich verhalten. Sie haben eine gleichnamige Funktion, die man zur Laufzeit aufrufen kann. Sie sind nicht gleich per Definition, sie verhalten sich nur gleich, das ist der Punkt hinter Duck Typing.

          dedlfix.

          1. ok das ist misst :/. Ich schreibe einen in php was auf.

            • Meinst Du das?
            class Duck {
              public function quack() {
                echo "quack";
              }
            }
            
            class Foo {
              public function quack() {
                Duck.quack();
              }
            }
            
            class Bar {
              public function quack() {
                Duck.quack();
              }
            }
            
            foo = new Foo;
            bar = new Bar;
            
            foo.quack();
            bar.quack();
            
            • Oder die Klasse Duck ersetzen durch die Funktion quack(). Duck in den Methoden fällt dann weg
            • Oder mit Interface
            interface Duck {
              quack();
            }
            

            und dann das implementieren in die jeweiligen Klassen dann aber jeweils definieren.

            class Foo implements Duck {
              public function quack() {
                echo "quack";
              }
            }
            

            Kommt irgend eins von denen an Ducktyping heran ?

            vlg MB

            1. Tach!

              Kommt irgend eins von denen an Ducktyping heran ?

              Nee, die Objekte brauchen keine Beziehungen zueinander haben. Ob sie eine haben oder nicht, ist irrelevant bei Duck Typing. Ob sie zum Quaken irgendwas aufrufen oder es selber tun, ist ebenfalls nicht weiter relevant. Und die konkreten Typen der Objekte spielen keine Rolle. Wichtig ist nur, dass sie dem Namen nach dieselben Eigenschaften/Methoden habe, die vom Verwender verlangt werden. Ein Objekt muss keine Ente sein, es muss auch keine Ente nehmen, um deren Eigenschaften nachzubilden, es muss sich nur wie eine Ente verhalten. Oder mehr programmierspezifisch: Mich interessiert nicht, welche Klasse du hast, ich will von dir nur auf Eigenschaft mit dem Namen x zugreifen oder die Methode mit dem Namen y aufrufen.

              dedlfix.

              1. jetzt verstehe ich. Sorry ich hab die 3 Methoden anders interpretiert als du sie hingeschrieben hast. Ich habs echt falsch gelesen. Sehr gute Beispiele!!!!!

                vlg MB

                ps: ich nehme an das du quack() einfach nur zur demo hardcoded hast.

                1. Tach!

                  ps: ich nehme an das du quack() einfach nur zur demo hardcoded hast.

                  Du meinst $duck->quack()? Nein, das habe ich so hingeschrieben, weil ich diese Methode aufrufen möchte und man das üblicherweise so macht. Ich muss nicht den Namen als String angeben oder andere Kopfstände machen, ich kann den ganz normal verwenden. Je nach Anforderung des Anwendungsfalls kann ich noch vor meinem Aufruf selbst prüfen, ob die Methode vorhanden ist, oder ich vertraue darauf und rufe sie einfach auf.

                  Es ist eines der Prinzipien von PHP, dass es zur Laufzeit das Objekt nimmt, das da grad in $duck liegt, dort schaut, ob es eine solche Methode hat, und die dann aufruft, oder eine Fehlermeldung ausgibt. Es arbeitet intern also auch nach den Duck-Type-Prinzip.

                  dedlfix.

    3. Tach!

      Was sind Ducktypes?

      Das ist kein Ding, das ist ein Konzept. Das kann man nur in schwach typisierten Sprachen verwenden, in anderen braucht es Interfaces oder Vergleichbares.

      Präzisierung: Duck typing geht auch in Sprachen mit normalerweise starker Typisierung, wenn diese eine dynamische Komponente enthalten. Zum Beispiel C# mit dem Typ dynamic. Da sorgt nicht bereits der Compiler für eine Typeinhaltung, sondern erst die Laufzeit versucht die Methoden aufzurufen, was gelingen oder fehlschlagen kann. Üblicherweise verwendet man da jedoch dynamic nicht ohne Not. Man will ja die Eigenschaften der starken Typsierung zum eigenen Vorteil nutzen, wenn man eine solche Sprache verwendet.

      dedlfix.

      1. Geht in C# auch für Objekte ohne dynamic, per Reflection. Es zeigt aber, wie anstrengend der Ententanz gelegentlich sein kann.

        public void foo(object duck)
        {
           if (duck != null)
           {
              MethodInfo mi = duck.GetType().GetMethod("quak", Type.EmptyTypes);
              if (mi != null)
                 mi.Invoke(duck, null);
           }
        // Oder mit neueren C# Sprachmitteln auch als Einzeiler:
           duck?.GetType().GetMethod("quak", Type.EmptyTypes)?.Invoke(duck, null);
        }
        
        1. Tach!

          Geht in C# auch für Objekte ohne dynamic, per Reflection. Es zeigt aber, wie anstrengend der Ententanz gelegentlich sein kann.

          Ja. Aber, liebe C#-Kinder, macht sowas nur, wenn sich keine elegantere Methode findet. Der Ententanz ist kein Stilmittel, das man um jeden Preis verwenden muss. Es ist eine Lösung für andere Sprachen, C# hat eine andere Philosophie.

          dynamic gibt es schon eine Weile (für den Notfall). Besser wäre jedoch auf ein Interface zu setzen und (in C#) die Ente Ente sein zu lassen. Setzt voraus, dass man das Interface erstellen und hinzufügen kann, und nicht mit unveränderlichen Fremdcode/-bibliotheken umgehen muss.

          dedlfix.

  2. moin community,

    zu Ducktype: man hat mir versucht zu erklärt was ducktype ist und mir den Spruch gesagt. Ich hab jetzt ne Ahnung aber keine Gewissheit. Was sind Ducktypes? Anwendungsbeispiele mit Metasyntaktischen Variablen + Scourcecode Doku ist extrem hilfreich.

    Nehmen wir mal an, Du hast eine Klasse Public::HTMLfile und einige hundert HTML-Dokumente, die über diese Klasse ausgeliefert werden. Dafür wird bei jedem Request eine Instanz dieser Klasse erstellt und welche Template-Datei da zu laden ist, bestimmt ein konfiguriertes Attribut.

    Nun sollen einige von den so ausgelieferten Webressourcen so erweitert werden dass sie Parameter entgegennehmen und verarbeiten können, bspw. Besucherkommentare. In Fakt wird hierzu eine Interface-Methode control() benötigt, aber die Instanz o.g. Klasse hat eine Solche Methode nicht.

    Duck-Typing nun, das ist die Lösung! Was für ein herrliches Buzzwort, ich kannte es noch nicht. Wenn Du magst, schick ich Dir ein Perl-Code-Beispiel für Duck-Typing -- Was eine Instanz im Nachhinein befähigt, bestimmte Methoden ausführen zu können -- und konfigurierbar isses auch noch ;)

    1. Duck-Typing nun, das ist die Lösung! Was für ein herrliches Buzzwort, ich kannte es noch nicht. Wenn Du magst, schick ich Dir ein Perl-Code-Beispiel für Duck-Typing -- Was eine Instanz im Nachhinein befähigt, bestimmte Methoden ausführen zu können -- und konfigurierbar isses auch noch ;)

      sehr gern :). Meine email addresse habe ich im Beitrag hinterlegt. Bin mal gespannt.

      vlg MB

      1. Duck-Typing nun, das ist die Lösung! Was für ein herrliches Buzzwort, ich kannte es noch nicht. Wenn Du magst, schick ich Dir ein Perl-Code-Beispiel für Duck-Typing -- Was eine Instanz im Nachhinein befähigt, bestimmte Methoden ausführen zu können -- und konfigurierbar isses auch noch ;)

        sehr gern :). Meine email addresse habe ich im Beitrag hinterlegt. Bin mal gespannt.

        Guck Dir schonmal das hier an. Damit präsentiere ich Dir meine gesamte Konfiguration, im Detail ist diese für jeden URL abrufbar. Das Duck Typing findet über das konfigurierbare Attribut interface = statt. Die Routing-Table zeigt Dir eine Übersicht -- welches Interface zu welchem URL konfiguriert ist.

        Über meine Konfiguration haben wir ja schon often diskutiert hier, begleitet von Zwischenrufen ;)