En quoi le typage de canard est-il différent de l’ancien type de variante et / ou des interfaces?

Je continue de voir la phrase “Duck Typing” sur laquelle on parle, et même sur un exemple de code ou deux. Je suis beaucoup trop paresseux pour faire mes propres recherches, quelqu’un peut-il me dire brièvement:

  • la différence entre un «type de canard» et un «type de variante» d’ancienne école, et
  • donner un exemple de l’endroit où je préférerais taper le canard sur le typage de variantes, et
  • fournir un exemple de quelque chose que je devrais utiliser le typage de canard pour accomplir?

illustration de canard gracieuseté du registre

Je ne veux pas paraître avare en doutant du pouvoir de cette «nouvelle» construction, et je ne cache pas le problème en refusant de faire la recherche, mais je bafoue tout le battage médiatique que j’ai vu à propos de c’est dernièrement Cela ne me ressemble pas (dactylographie dynamic), donc je ne vois pas les avantages tout de suite.

ADDENDA: Merci pour les exemples jusqu’à présent. Il me semble que l’utilisation de quelque chose comme ‘O-> can (Blah)’ équivaut à faire une recherche de reflection (ce qui n’est probablement pas bon marché), et / ou à peu près la même chose (O est IBlah) pouvoir vérifier pour vous, mais ce dernier a l’avantage de distinguer mon interface IBlah de votre interface IBlah alors que les deux autres ne le font pas. Certes, avoir beaucoup d’interfaces minuscules flottant pour chaque méthode serait désordonné, mais là encore, il est possible de vérifier de nombreuses méthodes individuelles …

… encore une fois je ne comprends pas. Est-ce un gain de temps fantastique ou la même chose dans un sac neuf? Où est l’exemple qui nécessite le typage de canard?

La réponse simple est que la variante est faiblement typée tandis que le typage de canard est fortement typé.

La saisie des canards peut se résumer comme suit: “si elle marche comme un canard, ressemble à un canard, agit comme un canard, alors c’est un canard.” Les termes informatiques considèrent que canard est l’interface suivante.

interface IDuck { void Quack(); } 

Examinons maintenant Daffy

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

Daffy n’est pas en réalité un IDuck dans ce cas. Pourtant, il agit comme un canard. Pourquoi Daffy doit-il implémenter IDuck alors qu’il est évident que Daffy est en fait un canard?

C’est là que le typage Duck entre en jeu. Il permet une conversion de type sécurisé entre tout type qui a tous les comportements d’un IDuck et une référence IDuck.

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

La méthode Quack peut maintenant être appelée sur “d” avec une sécurité de type complète. Il n’y a aucune chance d’erreur de type à l’exécution dans cette affectation ou cet appel de méthode.

Dans certaines des réponses, j’ai vu une utilisation incorrecte de la terminologie, ce qui a amené les gens à fournir des réponses erronées.

Donc, avant de donner ma réponse, je vais vous donner quelques définitions:

  1. Fortement typé

    Une langue est fortement typée si elle applique le type de sécurité d’un programme. Cela signifie qu’il garantit deux choses: quelque chose appelé le progrès et quelque chose d’autre appelé la conservation. Les progrès signifient essentiellement que tous les programmes “valablement typés” peuvent en fait être exécutés par l’ordinateur. Ils peuvent se bloquer, lancer une exception ou exécuter une boucle infinie, mais ils peuvent être exécutés. La préservation signifie que si un programme est “valablement tapé”, il sera toujours “validé correctement” et qu’aucune variable (ou emplacement de mémoire) ne contiendra une valeur non conforme au type qui lui est atsortingbué.

    La plupart des langues ont la propriété “progress”. Il y en a cependant beaucoup qui ne satisfont pas la propriété “préservation”. Un bon exemple est C ++ (et C aussi). Par exemple, il est possible en C ++ de contraindre n’importe quelle adresse mémoire à se comporter comme s’il s’agissait de n’importe quel type. Cela permet aux programmeurs de violer le système de saisie à tout moment. Voici un exemple simple:

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

    Ce code permet à quelqu’un de prendre un tableau de caractères et de lui écrire une instance “foo”. Si C ++ était fortement typé, cela ne serait pas possible. Tapez des langages sûrs, tels que C #, Java, VB, lisp, ruby, python et bien d’autres, si vous tentiez de convertir un tableau de caractères en instance “foo”.

  2. Faiblement typé

    Quelque chose est faiblement typé s’il n’est pas fortement typé.

  3. Typiquement statiquement

    Un langage est statiquement typé si son système de type est vérifié au moment de la compilation. Un langage typé statiquement peut être soit “faiblement typé” comme C, soit fortement typé comme C #.

  4. Dactylographié dynamicment

    Un langage typé dynamicment est un langage où les types sont vérifiés à l’exécution. De nombreuses langues ont un mélange quelconque de typage statique et dynamic. C #, par exemple, vérifiera dynamicment de nombreux casts lors de l’exécution, car il n’est pas possible de les vérifier au moment de la compilation. D’autres exemples sont des langages tels que Java, VB et Objective-C.

    Il y a aussi certaines langues qui sont “complètement” ou “principalement” typées dynamicment, comme “lisp”, “ruby” et “small talk”

  5. Duck typing

    Le typage de canard est quelque chose qui est complètement orthogonal au typage statique, dynamic, faible ou fort. C’est la pratique d’écrire du code qui fonctionnera avec un object indépendamment de son identité de type sous-jacente. Par exemple, le code VB.NET suivant:

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

    Travaillera, quel que soit le type d’object qui est passé dans “Foo”, à condition que soit défini une méthode appelée “Quack”. C’est-à-dire que si l’object ressemble à un canard, marche comme un canard et parle comme un canard, alors c’est un canard. La saisie du canard prend de nombreuses formes. Il est possible d’avoir du dactylographe statique, du typage dynamic du canard, du typage fort du canard et du typage hebdomadaire du canard. Les fonctions de template C ++ sont un bon exemple de “typage statique faible”. L’exemple montré dans la publication “JaredPar” montre un exemple de “fort typage statique de canard”. La liaison tardive en VB (ou le code en Ruby ou en Python) permet de “taper fort et dynamic”.

  6. Une variante

    Une variante est une structure de données à typage dynamic pouvant contenir une gamme de types de données prédéfinis, y compris des chaînes, des types d’entiers, des dates et des objects com. Il définit ensuite un ensemble d’opérations pour affecter, convertir et manipuler des données stockées dans des variantes. Le fait qu’un type soit fortement typé ou non dépend de la langue dans laquelle il est utilisé. Par exemple, une variante d’un programme VB 6 est fortement typée. Le runtime VB garantit que les opérations écrites en code VB seront conformes aux règles de saisie pour les variantes. Lier l’ajout d’une chaîne à un IUnknown via le type variant dans VB entraînera une erreur d’exécution. En C ++, cependant, les variantes sont faiblement typées car tous les types C ++ sont faiblement typés.

OK … maintenant que j’ai trouvé les définitions à l’écart, je peux maintenant répondre à votre question:

Une variante, en VB 6, permet une forme de saisie en canard. Il y a de meilleures façons de taper le canard (l’exemple de Jared Par est l’un des meilleurs), que les variantes, mais vous pouvez faire du typage avec des variantes. En d’autres termes, vous pouvez écrire un morceau de code qui fonctionnera sur un object, quelle que soit son identité de type sous-jacente.

Cependant, le faire avec des variantes ne donne pas vraiment de validation. Un mécanisme de type canard typé statiquement, comme celui décrit par JaredPar, offre les avantages du typage de canard, plus une validation supplémentaire du compilateur. Cela peut être vraiment utile.

Le typage de canard est juste un autre terme pour le typage dynamic ou la liaison tardive. Un object variant qui parsing / comstack avec n’importe quel access membre (par exemple, obj.Anything) qui peut ou non être défini lors de l’exécution est le type de canard.

Il est probable que rien ne nécessite la saisie de canards, mais cela peut être pratique dans certaines situations. Disons que vous avez une méthode qui prend et utilise un object de la classe scellée Duck de certaines bibliothèques tierces. Et vous voulez rendre la méthode testable. Et Duck a une API terriblement grande (un peu comme ServletRequest) dont vous n’avez besoin que d’un petit sous-ensemble. Comment le testez-vous?

Une façon consiste à faire en sorte que la méthode prenne quelque chose qui Ensuite, vous pouvez simplement créer un object simulant un charlatanisme.

Essayez de lire le tout premier paragraphe de l’article de Wikipedia sur le typage des canards.
Canard tapant sur Wikipedia

Je peux avoir une interface (IRunnable) qui définit la méthode Run ().
Si j’ai une autre classe avec une méthode comme celle-ci:
vide public RunSomeRunnable (IRunnable rn) {…}

Dans un langage convivial de type canard, je pouvais passer dans toute classe ayant une méthode Run () dans la méthode RunSomeRunnable ().
Dans un langage de type statique, la classe transmise à RunSomeRunnable doit implémenter explicitement l’interface IRunnable.

“Si ça marche () comme un canard”

variant est plus comme object dans .NET au moins.

@Kent Fredric

Votre exemple peut très certainement être fait sans avoir à taper du canard en utilisant des interfaces explicites … plus moche oui, mais ce n’est pas impossible.

Et personnellement, je trouve que des contrats bien définis dans les interfaces sont beaucoup plus efficaces pour faire respecter le code de la qualité que de se fier au typage des canards … mais ce n’est que mon avis et je le prends avec un grain de sel.

 public interface ICreature { } public interface IFly { fly();} public interface IWalk { walk(); } public interface IQuack { quack(); } // ETC // Animal Class public class Duck : ICreature, IWalk, IFly, IQuack { fly() {}; walk() {}; quack() {}; } public class Rhino: ICreature, IWalk { walk(); } // In the method List creatures = new List(); creatures.Add(new Duck()); creatures.Add(new Rhino()); foreach (ICreature creature in creatures) { if (creature is IFly) (creature as IFly).fly(); if (creature is IWalk) (creature as IWalk).walk(); } // Etc 

En ce qui concerne votre demande d’un exemple de quelque chose dont vous avez besoin d’utiliser le typage de canard pour accomplir, je ne pense pas qu’une telle chose existe. J’y pense comme si je pensais à utiliser la récursivité ou à utiliser l’itération. Parfois, l’un fonctionne mieux que l’autre.

D’après mon expérience, le typage de canard rend le code plus lisible et plus facile à saisir (à la fois pour le programmeur et le lecteur). Mais je trouve que le typage statique plus traditionnel élimine beaucoup d’erreurs de frappe inutiles. Il n’y a tout simplement aucun moyen de dire objectivement que l’un est meilleur qu’un autre ou même de dire quelles sont les situations les plus efficaces les unes par rapport aux autres.

Je dis que si vous êtes à l’aise avec le typage statique, utilisez-le. Mais vous devriez au moins essayer de le taper (et l’utiliser si possible dans un projet non sortingvial).

Pour vous répondre plus directement:

… encore une fois je ne comprends pas. Est-ce un gain de temps fantastique ou la même chose dans un sac neuf?

C’est les deux Vous attaquez toujours les mêmes problèmes. Vous faites juste ça d’une manière différente. Parfois, c’est tout ce que vous devez faire pour gagner du temps (même si pour aucune autre raison vous forcer à penser à faire quelque chose de différent).

Est-ce une panacée qui sauvera l’humanité entière de l’extinction? Non. Et quiconque vous dit le contraire est un fanatique.

Une variante (du moins comme je les ai utilisées dans VB6) contient une variable d’un type unique, bien défini, généralement statique. Par exemple, il peut contenir un int, un float ou une chaîne, mais les ints variantes sont utilisés en tant que ints, les flottants variant sont utilisés comme flottants et les chaînes de variantes sont utilisées en tant que chaînes.

Duck Typing utilise plutôt le typage dynamic. Sous le type de canard, une variable peut être utilisée comme un int, un float ou une chaîne, si elle prend en charge les méthodes particulières supscopes par un int ou un float ou une chaîne dans un contexte particulier.

Exemple de variantes versus typage de canard:

Pour une application Web, supposons que mes informations utilisateur proviennent de LDAP plutôt que d’une firebase database, mais je souhaite tout de même que mes informations utilisateur puissent être utilisées par le rest du framework Web, basé sur une firebase database et un ORM.

Utiliser des variantes: Pas de chance. Je peux créer une variante pouvant contenir un object UserFromDbRecord ou un object UserFromLdap, mais les objects UserFromLdap ne seront pas utilisables par les routines qui attendent des objects de la hiérarchie FromDbRecord.

Utiliser le typage de canard: je peux prendre ma classe UserFromLdap et append quelques méthodes qui le font agir comme une classe UserFromDbRecord. Je n’ai pas besoin de reproduire l’intégralité de l’interface FromDbRecord, juste assez pour les routines dont j’ai besoin. Si je le fais bien, c’est une technique extrêmement puissante et flexible. Si je le fais mal, cela produit un code très confus et fragile (sujet à la casse si la bibliothèque de bases de données ou la bibliothèque LDAP change).

Je pense que le point essentiel du typage de canard est la façon dont il est utilisé. On utilise la détection de méthode et l’introspection de l’entité pour savoir quoi en faire, au lieu de déclarer à l’avance ce qu’elle sera (où l’on sait quoi faire).

C’est probablement plus pratique dans les langages OO, où les primitives ne sont pas des primitives et sont plutôt des objects.

Je pense que la meilleure façon de résumer, en variante, une entité est / peut être n’importe quoi, et ce qu’elle est est incertain, par opposition à une entité qui ne ressemble à rien, mais vous pouvez déterminer ce qu’elle est en la demandant .

Voici quelque chose que je ne crois pas plausible sans le ducktyping.

 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 ); }); } 

Toute autre méthode exigerait que vous mettiez des ressortingctions de type pour les fonctions, ce qui éliminerait différentes espèces, nécessitant que vous créiez différentes fonctions pour différentes espèces, rendant le code vraiment infernal à maintenir.

Et ça craint vraiment d’essayer de faire une bonne chorégraphie.

Tout ce que vous pouvez faire avec le typage de canard, vous pouvez également le faire avec les interfaces. Duck-typing est rapide et confortable, mais certains soutiennent que cela peut conduire à des erreurs (si deux méthodes / propriétés distinctes sont nommées de la même manière). Les interfaces sont sûres et explicites, mais les gens pourraient dire “pourquoi énoncer une évidence?”. Le repos est une flamme. Tout le monde choisit ce qui lui convient et personne n’est “juste”.