2008-11-14 18 views
44

Ich sehe immer wieder die Phrase "Duck Typisierung", und lief sogar über ein oder zwei Codebeispiele. Ich bin viel zu faul beschäftigt, um meine eigene Forschung zu tun, kann mir jemand sagen, kurz:Wie unterscheidet sich die Eingabe von Ente von der alten 'Variante' und/oder den Schnittstellen?

  • der Unterschied zwischen einem ‚Ente Typ‘ und einem alten skool ‚Variante Typ‘, und
  • bieten ein Beispiel dafür, wo ich Ente Typisierung über Variante Typisierung bevorzugen könnte, und
  • bieten ein Beispiel für etwas, das ich haben würde Ente Typisierung verwenden, um zu erreichen?

duck typing illustration courtesy of The Register

meine ich nicht Geflügel scheinen durch Zweifel an der Macht dieses ‚neuen‘ konstruieren, und ich bin nicht das Problem ducken durch die Weigerung, die Forschung zu tun, aber ich bin quacking up Bei all dem Flock-Hype, den ich in letzter Zeit gesehen habe. Es sieht aus wie keine Eingabe (aka dynamische Eingabe) zu mir, so sehe ich nicht sofort die Vorteile.

Nachtrag: Danke für die bisherigen Beispiele. Es scheint mir, dass die Verwendung von etwas wie "O-> can (Blah)" einer Reflektionssuche gleichkommt (was wahrscheinlich nicht billig ist) und/oder ungefähr dasselbe ist wie das Sprechen des Compilers (O is IBlah) in der Lage sein, nach Ihnen zu suchen, aber Letzteres hat den Vorteil, dass Sie meine IBlah-Schnittstelle von Ihrer IBlah-Schnittstelle unterscheiden, während die anderen beiden dies nicht tun. Zugegeben, eine Menge von kleinen Schnittstellen für jede Methode zu schweben würde chaotisch werden, aber dann wieder so für eine Vielzahl von einzelnen Methoden zu überprüfen ...

... so wieder ich bin gerade nicht bekommen es. Ist es eine fantastische Zeitersparnis oder das gleiche alte Ding in einem brandneuen Sack? Wo ist das Beispiel, dass erfordert Ente Typisierung?

+0

Ich denke, Sie meinen nicht "Variante", sondern eher spät verbindlich durch IDispatch-Schnittstelle. – wasker

+0

schön gemacht! Sehr schön gemacht. Danke, dass du mich heute lächeln lässt. – Tim

+0

Arbeitet etwas, um den Enten das Tippen beizubringen, aber die Produktivität lohnt sich sehr ;-) – Rudiger

Antwort

18

Die einfache Antwort ist Variante ist schwach typisiert während Ente Eingabe ist stark typisiert.

Ente tippen kann gut zusammengefasst werden als "wenn es wie eine Ente geht, sieht aus wie eine Ente, verhält sich wie eine Ente, dann ist es eine Ente." IT-Begriffe betrachten Ente als die folgende Schnittstelle.

interface IDuck { 
    void Quack(); 
} 

Lassen Sie uns nun Daffy untersuchen

class Daffy { 
    void Quack() { 
    Console.WriteLine("Thatsssss dispicable!!!!"); 
    } 
} 

Daffy ist nicht wirklich ein iDuck in diesem Fall. Und doch verhält es sich wie eine Ente. Warum sollte Daffy IDuck implementieren, wenn es ziemlich offensichtlich ist, dass Daffy tatsächlich eine Ente ist?

Hier kommt die Eingabe von Duck ins Spiel. Sie ermöglicht eine typsichere Konvertierung zwischen jedem Typ, der alle Verhaltensweisen eines IDuck und einer IDuck-Referenz aufweist.

IDuck d = new Daffy(); 
d.Quack(); 

Die Quack-Methode kann nun mit vollständiger Typensicherheit auf "d" aufgerufen werden. Bei dieser Zuweisung oder diesem Methodenaufruf besteht keine Möglichkeit eines Laufzeittypfehlers.

+7

Was ich an Ducktyping nicht verstehe, ist folgendes: Wie können Sie sicher sein, dass Daffys Quack-Methode mit IDuck.Quack identisch ist? Nur weil es denselben Namen hat und der Typ der Parameter übereinstimmt, bedeutet das nicht unbedingt, dass die Methode das tut, was Sie wollen/brauchen. ... – Otherside

+1

... Wenn Daffy die IDuck-Schnittstelle explizit implementiert, sind Sie sicherer, dass Sie die richtige Methode aufrufen. Zum Beispiel könnte eine Refresh-Methode bedeuten: Aktualisieren/Repaint der GUI oder Neuladen von Daten aus der Datenbank und Aktualisieren dieses Objekts. – Otherside

+5

Das ist die Gefahr bei der Eingabe von Enten - es gibt keine semantische Prüfung, nur syntaktische Prüfung. Aber nur weil Sie "sicherer" sind, wenn Sie mit einem Typ arbeiten, der eine Schnittstelle implementiert, bedeutet das nicht, dass Sie "sicher" sind. –

5

Duck Typisierung ist nur ein anderer Begriff für dynamische Typisierung oder späte Bindung. Ein variantes Objekt, das mit jedem Mitgliedzugriff (z. B. obj.Anything), der während der Laufzeit möglicherweise nicht definiert werden kann, analysiert/kompiliert wird, ist die Enttippung.

+0

+1 Danke - ich habe eine Anfrage für Beispiele hinzugefügt, wenn du auch eine Aufnahme machen willst. –

1

Ich denke, der Kernpunkt der Ente Typisierung ist, wie es verwendet wird. Man verwendet Methodenerkennung und Introspektion der Entität, um zu wissen, was damit zu tun ist, anstatt vorher zu deklarieren, was sein soll (wo Sie wissen, was damit zu tun ist).

Es ist wahrscheinlich praktischer in OO-Sprachen, wo Primitive keine Primitiven sind und stattdessen Objekte sind.

Ich denke, der beste Weg, um es zusammenzufassen, in Variant-Typ, eine Entität ist/kann alles sein, und es was unsicher ist, nur auf eine Einheit im Gegensatz sieht wie alles, aber sie arbeiten können, was ist es, indem Sie es fragen.

Hier ist etwas, was ich glaube nicht plausibel ist, ohne Duck-Typing.

sub dance { 
    my $creature = shift; 
    if($creature->can("walk")){ 
     $creature->walk("left",1); 
     $creature->walk("right",1); 
     $creature->walk("forward",1); 
     $creature->walk("back",1); 
    } 
    if($creature->can("fly")){ 
      $creature->fly("up"); 
      $creature->fly("right",1); 
      $creature->fly("forward",1); 
      $creature->fly("left", 1); 
      $creature->fly("back", 1); 
      $creature->fly("down"); 
    } else if ($creature->can("walk")) { 
     $creature->walk("left",1); 
     $creature->walk("right",1); 
     $creature->walk("forward",1); 
     $creature->walk("back",1); 
    } else if ($creature->can("splash")) { 
     $creature->splash("up") for (0 .. 4); 
    } 
    if($creature->can("quack")) { 
     $creature->quack(); 
    } 
} 

my @x =(); 
push @x, new Rhinoceros ; 
push @x, new Flamingo; 
push @x, new Hyena; 
push @x, new Dolphin; 
push @x, new Duck; 

for my $creature (@x){ 

    new Thread(sub{ 
     dance($creature); 
    }); 
} 

Jede andere Art und Weise würden Sie erfordern Typ Beschränkungen für Funktionen zu setzen, die verschiedene Arten ausgeschnitten würde, benötigen Sie verschiedene Funktionen für verschiedene Arten zu erstellen, zu pflegen den Code wirklich höllisch zu machen.

Und das saugt wirklich in Bezug auf die gerade versuchen, eine gute Choreographie auszuführen.

+0

danke für das Beispiel ! Könnte ich nicht dasselbe erreichen, wenn ich IWalk-, IFly-, ISplash- und IQuack-Schnittstellen hätte? Zugegeben, das wäre schmerzhafter ... alternativ könnte ich Reflektionen verwenden, um zu sehen, ob die Methoden verfügbar sind (was wahrscheinlich der "Dosen" -Operator unter der Haube tut) ...? –

+1

Nun, ich denke, im Wesentlichen gibt es keinen/direkten/Unterschied, Sie können dynamische Typisierung in statischen Sprachen virtuell tun, wenn Sie ** hart genug versuchen, C++ hat eine Lamda-Implementierung zum Beispiel. Nur die Sprache ist um diesen Stil herum zentriert, was es einfacher macht. –

1

Eine Variante (mindestens so wie ich sie in VB6 verwendet habe) enthält eine Variable eines einzelnen, wohldefinierten, normalerweise statischen Typs. Z. B. könnte es ein int oder ein float oder eine Zeichenkette enthalten, aber Variantenints werden als Ints verwendet, Variantenfloats werden als Floats verwendet, und Variantenstrings werden als Strings verwendet.

Bei der Duck-Typisierung wird dynamische Typisierung verwendet. Bei der Enttippung kann eine Variable als int, float oder string verwendet werden, wenn sie die bestimmten Methoden unterstützt, die ein int oder float oder eine Zeichenfolge in einem bestimmten Kontext unterstützt.

Beispiel von Varianten im Vergleich zu Ente eingeben:

Für eine Web-Anwendung, nehme ich meine Benutzerdaten aus LDAP kommen will, anstatt aus einer Datenbank, aber ich will noch meine Benutzerinformationen durch den Rest verwendbar sein, das Web-Framework, das auf einer Datenbank und einem ORM basiert.

Verwenden von Varianten: Kein Glück. Ich kann eine Variante erstellen, die ein UserFromDbRecord-Objekt oder ein UserFromLdap-Objekt enthalten kann, aber UserFromLdap-Objekte können nicht von Routinen verwendet werden, die Objekte aus der FromDbRecord-Hierarchie erwarten.

Verwenden von Duck Typisierung: Ich kann meine UserFromLdap-Klasse nehmen und ein paar Methoden hinzufügen, die es wie eine UserFromDbRecord-Klasse verhalten. Ich muss nicht die gesamte FromDbRecord-Schnittstelle replizieren, gerade genug für die Routinen, die ich verwenden muss. Wenn ich das richtig mache, ist es eine extrem leistungsfähige und flexible Technik. Wenn ich es falsch mache, produziert es sehr verwirrenden und spröden Code (bruchgefährdet, wenn sich entweder die DB-Bibliothek oder die LDAP-Bibliothek ändert).

+0

danke für das Beispiel! also kann ich einfach die Methoden hinzufügen, die ich brauche, anstatt jede Methode hinzuzufügen, die von einer Schnittstelle benötigt wird (oder die Basisklasse zu ändern) - das ist ziemlich cool. Wird das nicht verwirrend, wenn Sie es oft tun? –

3

Versuchen Sie, den allerersten Absatz des Wikipedia-Artikels auf Ente Typisierung zu lesen.
Duck typing on Wikipedia

Ich kann eine Schnittstelle (IRunnable) haben, die die Methode Run() definiert.
Wenn ich eine andere Klasse mit einem Verfahren wie dieser:
public void RunSomeRunnable (IRunnable rn) {...}

In einer Ente Art freundliche Sprache kann ich in jeder Klasse übergeben, die einen Run hatten() -Methode in die RunSomeRunnable() -Methode.
In einer statisch typisierten Sprache muss die in RunSomeRunnable übergebene Klasse die IRunnable-Schnittstelle explizit implementieren.

"Wenn es Run() wie eine Ente"

Variante eher wie Objekt in .NET zumindest ist.

+0

+1 danke - ich habe eine Anfrage für Beispiele hinzugefügt, wenn Sie auch eine Aufnahme machen möchten –

+1

Ich kann nicht glauben, dass Sie in diesem Beispiel die Methode Quack() verpasst haben;) – korona

2

@Kent Fredric

Ihr Beispiel kann sicherlich ohne Ente eingeben, indem Sie explizite Schnittstellen erfolgen ... hässlicher ja, aber es ist nicht unmöglich ist.

Und persönlich, finde ich mit gut definierten Verträge in Schnittstellen viel besser für die Qualität Code Durchsetzung, als sich auf Duck Typing ... aber das ist nur meine Meinung und nehmen Sie es mit einem Körnchen Salz.

+0

sind Sie nicht auf Jon Skeet warten, um zuerst alle Fragen zu beantworten? ;-) –

+0

Ich denke, er schläft (Wie spät ist es in Großbritannien?) – FlySwat

+0

es ist derzeit 4:47 in Großbritannien –

32

In einigen der Antworten hier, habe ich einige falsche Verwendung der Terminologie gesehen, was dazu geführt hat, dass Menschen falsche Antworten geben.

Also, bevor ich meine Antwort geben, ich werde ein paar Begriffe zu definieren:

  1. getippt Stark

    Eine Sprache stark typisiert ist, wenn er die Typsicherheit eines Programms erzwingt. Das bedeutet, dass es zwei Dinge garantiert: etwas, das Fortschritt genannt wird, und etwas, das man Konservierung nennt. Fortschritt bedeutet im Grunde, dass alle "gültig typisierten" Programme tatsächlich vom Computer ausgeführt werden können. Sie können abstürzen oder eine Ausnahme auslösen oder für eine Endlosschleife laufen, aber sie können tatsächlich ausgeführt werden. Preservation bedeutet, dass, wenn ein Programm "gültig typisiert" ist, es immer "Gültig eingegeben" ist, und dass keine Variable (oder Speicherort) einen Wert enthält, der nicht dem zugewiesenen Typ entspricht.

    Die meisten Sprachen haben die Eigenschaft "progress". Es gibt jedoch viele, die die Eigenschaft "Erhaltung" nicht erfüllen. Ein gutes Beispiel ist C++ (und auch C). Zum Beispiel ist es in C++ möglich, jede Speicheradresse so zu verhalten, als wäre sie ein beliebiger Typ. Dies ermöglicht es Programmierern grundsätzlich, jederzeit gegen das Typsystem zu verstoßen. Hier ist ein einfaches Beispiel:

    struct foo 
    { 
        int x; 
        iny y; 
        int z; 
    } 
    
    char * x = new char[100]; 
    foo * pFoo = (foo *)x; 
    foo aRealFoo; 
    *pFoo = aRealFoo; 
    

    Mit diesem Code kann jemand eine Reihe von Zeichen nehmen und eine „foo“ Instanz schreiben. Wenn C++ stark typisiert wäre, wäre dies nicht möglich. Geben Sie sichere Sprachen ein, wie C#, Java, VB, Lisp, Ruby, Python und viele andere, würde eine Ausnahme auslösen, wenn Sie versuchen würden, ein Array von Zeichen an eine "foo" -Instanz zu übertragen.

  2. getippt Schwach

    Etwas schwach, wenn es nicht stark typisiert wird eingegeben wird.

  3. Statisch typisierte

    Eine Sprache statisch bei verifiziert wird der Kompilierung, wenn sein Typ-System eingegeben wird. Eine statisch typisierte Sprache kann entweder "schwach typisiert" wie C oder stark typisiert wie C# sein.

  4. getippt dynamisch

    Eine dynamisch typisierte Sprache ist eine Sprache, wo Typen zur Laufzeit überprüft werden. Viele Sprachen haben eine Mischung aus statischer und dynamischer Typisierung. C# zum Beispiel wird viele Umwandlungen dynamisch zur Laufzeit überprüfen, da es nicht möglich ist, sie zur Kompilierzeit zu überprüfen.Andere Beispiele sind Sprachen wie Java, VB und Objective-C.

    Es gibt auch einige Sprachen, die „vollständig“ oder „meistens“ dynamisch typisierte, wie „Lispeln“, „Rubin“ sind und „small talk“

  5. Ente

    Ente Schreiben Schreiben ist etwas, das ist völlig orthogonal zu statischer, dynamischer, schwacher oder starker Typisierung. Es ist die Praxis, Code zu schreiben, der mit einem Objekt unabhängig von seiner zugrunde liegenden Typidentität arbeitet. Zum Beispiel kann der folgende VB.NET-Code:

    function Foo(x as object) as object 
        return x.Quack() 
    end function 
    

    funktionieren wird, unabhängig davon, was die Art des Objekts ist, dass in „Foo“ übergeben wird, vorausgesetzt, dass ist definiert eine Methode namens „Quack“. Das heißt, wenn das Objekt wie eine Ente aussieht, wie eine Ente läuft und sich wie eine Ente verhält, dann ist es eine Ente. Ente tippen kommt in vielen Formen. Es ist möglich, statische Ente tippen, dynamische Ente tippen, starke Ente tippen und Ente tippen. C++ Template-Funktionen sind ein gutes Beispiel für "schwache statische Duck-Typisierung". Das Beispiel in "JaredPar's" zeigt ein Beispiel für "strong static duck typing". Late Binding in VB (oder Code in Ruby oder Python) ermöglicht "starke dynamische Ente Typisierung".

  6. Variant

    Eine Variante ist eine dynamisch typisierten Datenstruktur, die eine Reihe von vordefinierten Datentypen aufnehmen kann, einschließlich Zeichenfolge, integer-Typen, Datumsangaben und COM-Objekte. Es definiert dann eine Reihe von Operationen zum Zuweisen, Konvertieren und Manipulieren von Daten, die in Varianten gespeichert sind. Ob eine Variante stark typisiert ist oder nicht, hängt von der Sprache ab, in der sie verwendet wird. Zum Beispiel ist eine Variante in einem VB 6-Programm stark typisiert. Die VB-Laufzeitumgebung stellt sicher, dass in VB-Code geschriebene Operationen den Typisierungsregeln für Varianten entsprechen. Das Verknüpfen eines Strings mit einem IUnknown über den Variant-Typ in VB führt zu einem Laufzeitfehler. In C++ sind Varianten jedoch schwach typisiert, da alle C++ - Typen schwach typisiert sind.

OK .... jetzt, dass ich die Definitionen bekommen aus dem Weg, ich jetzt Ihre Frage beantworten kann:

Eine Variante, in VB 6 ermöglicht eine Form Ente eingeben zu tun. Es gibt bessere Möglichkeiten, Enten zu tippen (Jared Pars Beispiel ist eine der besten), als Varianten, aber Sie können Ente mit Varianten tippen. Das heißt, Sie können einen Codeabschnitt schreiben, der unabhängig von der zugrunde liegenden Typidentität auf einem Objekt ausgeführt wird.

Allerdings macht es nicht viel Validierung mit Varianten. Ein statisch typisierter Ducktyp-Mechanismus, wie der von JaredPar beschriebene Mechanismus, bietet die Vorteile der Duck-Typisierung sowie eine zusätzliche Validierung durch den Compiler. Das kann sehr hilfreich sein.

+3

Danke für die erweiterten Informationen, aber ich bin neugierig auf eine Aussage: "Etwas wird wöchentlich getippt, wenn es nicht stark typisiert ist." - Würde etwas nicht wöchentlich getippt, wenn es nur einmal pro Woche getippt würde? ;-) –

+2

Ja ... Ich bin nicht der Beste bei der Rechtschreibprüfung meiner Posts. –

2

In Bezug auf Ihre Anfrage für ein Beispiel für etwas, das Sie benötigen Ente Eingabe zu erreichen, ich glaube nicht, dass so etwas existiert. Ich denke darüber nach, ob ich darüber nachdenke, ob ich eine Rekursion oder eine Iteration verwenden soll. Manchmal funktioniert es einfach besser als das andere.

Nach meiner Erfahrung macht die Eingabe von Enten Code lesbarer und leichter zu verstehen (sowohl für den Programmierer als auch für den Leser). Aber ich finde, dass traditionellere statische Typisierung viele unnötige Tippfehler eliminiert. Es gibt einfach keine Möglichkeit objektiv zu sagen, dass einer besser ist als der andere oder sogar zu sagen, in welchen Situationen einer effektiver ist als der andere.

Ich sage, dass wenn Sie mit statischen Typisierung vertraut sind, dann verwenden Sie es. Aber du solltest zumindest versuchen, Ente zu tippen (und sie wenn möglich in einem nicht-trivialen Projekt zu verwenden).

Um Ihnen mehr direkt zu beantworten:

... ich einfach nicht so wieder bin immer. Ist es eine fantastische Zeitersparnis oder das gleiche alte Ding in einem brandneuen Sack?

Es ist beides. Sie greifen immer noch dieselben Probleme an. Du machst es einfach anders. Manchmal ist das wirklich alles, was Sie tun müssen, um Zeit zu sparen (selbst wenn Sie sich aus einem anderen Grund dazu gezwungen sehen, etwas anders zu machen).

Ist es ein Allheilmittel, das die gesamte Menschheit vor dem Aussterben bewahren wird? Nein. Und wer Ihnen etwas anderes sagt, ist ein Eiferer.

4

Wahrscheinlich nichts erfordert Duck-Typisierung, aber es kann in bestimmten Situationen bequem sein. Angenommen, Sie haben eine Methode, die ein Objekt der versiegelten Klasse Duck aus einer Bibliothek eines Drittanbieters übernimmt und verwendet. Und Sie möchten die Methode testbar machen. Und Duck hat eine ziemlich große API (so ähnlich wie ServletRequest), von der man sich nur um eine kleine Teilmenge kümmern muss. Wie testen Sie es?

Ein Weg ist es, die Methode etwas nehmen, die quakt. Dann können Sie einfach ein Quacksalberobjekt erstellen.

0

Alles was du mit Duck-Tipping machen kannst, kannst du auch mit Interfaces machen. Duck-Typing ist schnell und bequem, aber einige argumentieren, dass es zu Fehlern führen kann (wenn zwei verschiedene Methoden/Eigenschaften gleich benannt werden). Schnittstellen sind sicher und explizit, aber die Leute sagen vielleicht "Warum das Offensichtliche?". Ruhe ist eine Flamme. Jeder wählt, was ihm passt und niemand ist "richtig".