Quels sont les avantages de la saisie en canard par rapport à la saisie en canard ?typage statique ?

StackOverflow https://stackoverflow.com/questions/47972

  •  09-06-2019
  •  | 
  •  

Question

Je recherche et expérimente davantage avec Groovy et j'essaie de comprendre les avantages et les inconvénients de l'implémentation de choses dans Groovy que je ne peux pas/ne fais pas en Java.La programmation dynamique n'est encore qu'un concept pour moi puisque je suis profondément imprégné des langages statiques et fortement typés.

Groovy me donne la possibilité de type canard, mais je n'en vois pas vraiment la valeur.En quoi la saisie en canard est-elle plus productive que la saisie statique ?Quel genre de choses puis-je faire dans ma pratique du code pour m'aider à en comprendre les avantages ?

Je pose cette question en pensant à Groovy, mais je comprends que ce n'est pas nécessairement une question Groovy, donc j'apprécie les réponses de tous les camps de code.

Était-ce utile?

La solution

Ensuite, ce qui est mieux :EMACS ou vi ?C'est l'une des guerres de religion en cours.

Pense-y de cette façon:tout programme qui est correct, sera correct si la langue est typée statiquement.Le typage statique permet au compilateur de disposer de suffisamment d'informations pour détecter les incompatibilités de types au moment de la compilation plutôt qu'au moment de l'exécution.Cela peut être gênant si vous effectuez des programmes incrémentiels, même si (je maintiens) si vous réfléchissez clairement à votre programme, cela n'a pas beaucoup d'importance ;d'un autre côté, si vous construisez un très gros programme, comme un système d'exploitation ou un commutateur téléphonique, avec des dizaines, des centaines ou des milliers de personnes qui travaillent dessus, ou avec des exigences de fiabilité très élevées, alors le compilateur sera capable de détectez une large classe de problèmes pour vous sans avoir besoin d'un scénario de test pour déterminer le bon chemin de code.

Ce n'est pas comme si le typage dynamique était une chose nouvelle et différente :C, par exemple, est effectivement typé dynamiquement, puisque je peux toujours lancer un foo* à un bar*.Cela signifie simplement qu'il est de ma responsabilité en tant que programmeur C de ne jamais utiliser de code approprié sur un bar* quand l'adresse pointe vraiment vers un foo*.Mais en raison des problèmes rencontrés avec les grands programmes, C a développé des outils comme lint(1), renforcé son système de types avec typedef et a finalement développé une variante fortement typée en C++.(Et, bien sûr, C++ a à son tour développé des moyens de contourner le typage fort, avec toutes les variétés de transtypages et de génériques/modèles et avec RTTI.

Une autre chose, cependant : ne confondez pas « programmation agile » avec « langages dynamiques ». Programmation agile concerne la façon dont les gens travaillent ensemble dans un projet :le projet peut-il s'adapter aux exigences changeantes pour répondre aux besoins des clients tout en maintenant un environnement humain pour les programmeurs ?Cela peut être fait avec des langages typés dynamiquement, et c'est souvent le cas, car ils peuvent être plus productifs (par exemple, Ruby, Smalltalk), mais cela peut être fait, et cela a été fait avec succès, en C et même en assembleur.En fait, Développement de rallye utilise même des méthodes agiles (SCRUM notamment) pour faire du marketing et de la documentation.

Autres conseils

De nombreux commentaires sur le typage des canards ne justifient pas vraiment les affirmations.Ne pas "avoir à se soucier" d'un type n'est pas durable pour la maintenance ou pour rendre une application extensible.J'ai vraiment eu une bonne occasion de voir Grails en action lors de mon dernier contrat et c'est vraiment assez drôle à regarder.Tout le monde est satisfait des gains liés à la possibilité de "créer une application" et de commencer - malheureusement, tout vous rattrape en arrière-plan.

Groovy me semble la même chose.Bien sûr, vous pouvez écrire du code très succinct et il y a certainement du sucre dans la façon dont nous travaillons avec les propriétés, les collections, etc...Mais le coût de ne pas savoir ce qui se passe ne fait qu’empirer.À un moment donné, vous vous grattez la tête en vous demandant pourquoi le projet est devenu 80 % de tests et 20 % de travail.La leçon ici est que « plus petit » ne signifie pas un code « plus lisible ».Désolé les amis, c'est une logique simple : plus vous devez en savoir intuitivement, plus le processus de compréhension de ce code devient complexe.C'est pourquoi les interfaces graphiques ont cessé de devenir trop emblématiques au fil des ans - c'est sûr que c'est joli, mais ce qui se passe n'est pas toujours évident.

Les personnes participant à ce projet semblaient avoir du mal à « comprendre » les leçons apprises, mais lorsque vous avez des méthodes renvoyant soit un seul élément de type T, un tableau de T, un ErrorResult ou un null...cela devient plutôt évident.

Cependant, travailler avec Groovy a fait une chose pour moi : des heures facturables impressionnantes !

Il n'y a rien de mal avec le typage statique si vous utilisez Haskell, qui possède un système de type statique incroyable.Cependant, si vous utilisez des langages comme Java et C++ qui ont des systèmes de types terriblement paralysants, le typage canard est définitivement une amélioration.

Imaginez essayer d'utiliser quelque chose d'aussi simple que "carte" en Java (et non, je ne veux pas dire la structure des données).Même les génériques sont plutôt mal pris en charge.

La saisie en canard paralyse la vérification statique de la plupart des IDE modernes, qui peuvent signaler des erreurs lors de la frappe.Certains considèrent cela comme un avantage.Je veux que l'EDI/compilateur me dise que j'ai fait un stupide truc de programmeur dès que possible.

Mon argument préféré le plus récent contre le typage du canard provient d'un projet Grails DTO :

class SimpleResults {
    def results
    def total
    def categories
}

results il s'avère que c'est quelque chose comme Map<String, List<ComplexType>>, qui ne peut être découvert qu'en suivant une série d'appels de méthodes dans différentes classes jusqu'à ce que vous trouviez où il a été créé.Pour les curieux en phase terminale, total est la somme des tailles des List<ComplexType>sable categories est la taille du Map

Cela était peut-être clair pour le développeur d'origine, mais le pauvre responsable de la maintenance (ME) a ​​perdu beaucoup de cheveux en traquant celui-ci.

Il est un peu difficile de voir la valeur du duck typing tant que vous ne l'avez pas utilisé pendant un petit moment.Une fois que vous vous y serez habitué, vous réaliserez à quel point cela représente une charge pour votre esprit de ne pas avoir à gérer les interfaces ou à vous soucier du type exact de quelque chose.

À mon humble avis, l'avantage du typage canard est amplifié lorsque vous adhérez à certaines conventions, telles que nommer vos variables et méthodes de manière cohérente.En prenant l'exemple de Ken G., je pense qu'il serait préférable de lire :

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

Supposons que vous définissiez un contrat sur une opération nommée « calculateRating(A,B) » où A et B adhèrent à un autre contrat.En pseudocode, cela donnerait :

Long calculateRating(A someObj, B, otherObj) {

   //some fake algorithm here:
   if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating());
   else return otherObj.calcRating();

}

Si vous souhaitez implémenter cela en Java, A et B doivent implémenter une sorte d'interface qui ressemble à ceci :

public interface MyService {
    public int doStuff(String input);
}

Par ailleurs, si vous souhaitez généraliser votre contrat de calcul de notes (disons que vous disposez d'un autre algorithme de calcul de notes), vous devez également créer une interface :

public long calculateRating(MyService A, MyServiceB);

Avec la frappe au canard, vous pouvez abandonner votre interfaces et comptez simplement sur le temps d'exécution, A et B répondront correctement à votre doStuff() appels.Il n’est pas nécessaire de définir une définition spécifique du contrat.Cela peut fonctionner pour vous, mais cela peut aussi jouer contre vous.

L'inconvénient est que vous devez être très prudent afin de garantir que votre code ne sera pas brisé lorsque d'autres personnes le modifieront (c'est-à-dire que l'autre personne doit être consciente du contrat implicite sur le nom de la méthode et les arguments).

Notez que cela s'aggrave particulièrement en Java, où la syntaxe n'est pas aussi concise qu'elle pourrait l'être (par rapport à Échelle Par exemple).Un contre-exemple en est le Cadre d'ascenseur, où ils disent que le nombre de SLOC du framework est similaire à Des rails, mais le code de test comporte moins de lignes car ils n'ont pas besoin d'implémenter de vérifications de type dans les tests.

Voici un scénario dans lequel la saisie en canard permet d'économiser du travail.

Voici un cours très trivial

class BookFinder {
    def searchEngine

    def findBookByTitle(String title) {
         return searchEngine.find( [ "Title" : title ] ) 
    }
}

Passons maintenant au test unitaire :

void bookFinderTest() {
    // with Expando we can 'fake' any object at runtime.
    // alternatively you could write a MockSearchEngine class.
    def mockSearchEngine = new Expando()
    mockSearchEngine.find = {
        return new Book("Heart of Darkness","Joseph Conrad")
    }

    def bf = new BookFinder()
    bf.searchEngine = mockSearchEngine
    def book = bf.findBookByTitle("Heart of Darkness")
    assert(book.author == "Joseph Conrad"
}

Nous avons pu remplacer le SearchEngine par un Expando, en raison de l'absence de vérification de type statique.Avec la vérification de type statique, nous aurions dû nous assurer que SearchEngine était une interface, ou au moins une classe abstraite, et en créer une implémentation simulée complète.Cela demande beaucoup de travail, ou vous pouvez utiliser un cadre de simulation sophistiqué à usage unique.Mais le typage canard est à usage général et nous a aidé.

En raison du typage duck, notre test unitaire peut fournir n'importe quel ancien objet à la place de la dépendance, à condition qu'il implémente les méthodes appelées.

Pour souligner, vous pouvez le faire dans un langage typé statiquement, avec une utilisation prudente des interfaces et des hiérarchies de classes.Mais avec la frappe au canard, vous pouvez le faire avec moins de réflexion et moins de frappes.

C'est un avantage du typage canard.Cela ne signifie pas que le typage dynamique soit le bon paradigme à utiliser dans toutes les situations.Dans mes projets Groovy, j'aime revenir à Java dans des circonstances où je pense que les avertissements du compilateur concernant les types vont m'aider.

Avec, TDD + couverture du code à 100 % + Outils IDE pour exécuter mes tests en permanence, je ne ressens plus le besoin de typage statique.Sans types forts, mes tests unitaires sont devenus si simples (utilisez simplement Maps pour créer des objets fictifs).Surtout, lorsque vous utilisez des génériques, vous pouvez voir la différence :

//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>>

contre

//Dynamic typing
def someMap = [:]   

Ce n'est pas que le typage canard soit plus productif que le typage statique, mais plutôt qu'il est simplement différent.Avec le typage statique, vous devez toujours vous soucier que vos données soient du type correct et en Java, elles apparaissent via la conversion vers le bon type.Avec le typage duck, le type n'a pas d'importance tant qu'il a la bonne méthode, donc cela élimine vraiment beaucoup de tracas liés au casting et aux conversions entre les types.

@Chris Bunch

Ce n'est pas que le typage statique soit plus productif que le typage canard, mais simplement qu'il est différent.Avec la saisie au canard, vous devez toujours vous soucier que vos données aient la bonne méthode et en Javascript ou Ruby, elles apparaissent grâce à de nombreux tests de méthode.Avec le typage statique, cela n'a pas d'importance tant qu'il s'agit de la bonne interface, cela élimine donc simplement une grande partie des tracas liés aux tests et aux conversions entre les types.

Désolé mais je devais le faire...

Mon avis:

Les langages typés dynamiquement ou typés canard sont des jouets.Vous ne pouvez pas obtenir Intellisense et vous perdez du temps de compilation (ou du temps d'édition - lorsque vous utilisez un VRAI IDE comme VS, pas ces déchets que d'autres pensent être des IDE) pour la validation du code.

Restez à l’écart de tout langage qui n’est pas typé statiquement, tout le reste n’est que du simple masochisme.

Pour moi, ils ne sont pas terriblement différents si vous considérez les langages typés dynamiquement comme simplement une forme de typage statique où tout hérite d'une classe de base suffisamment abstraite.

Des problèmes surviennent lorsque, comme beaucoup l’ont souligné, cela commence à vous paraître étrange.Quelqu'un a souligné une fonction qui renvoie un seul objet, une collection ou une valeur nulle.Demandez à la fonction de renvoyer un type spécifique, pas plusieurs.Utilisez plusieurs fonctions pour un seul ou une collection.

Cela revient à dire que n’importe qui peut écrire du mauvais code.La frappe statique est un excellent dispositif de sécurité, mais parfois le casque gêne lorsque vous voulez sentir le vent dans vos cheveux.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top