Je n'ai jamais eu l'idée des assertions - pourquoi devriez-vous les utiliser?
Je veux dire, disons que j'étais un pilote de formule et que toutes les affirmations étaient des choses comme une ceinture de sécurité, un casque, etc.
Les tests (en débogage) étaient tous corrects, mais maintenant nous voulons faire de la course! Devrions-nous abandonner toute la sécurité, car il n'y avait pas de problèmes lors des tests?
Je ne les enlèverai jamais. Je pense que la plupart des personnes qui affirment que supprimer quelque chose de comparable aux assertions ne profilent jamais leur code ou que les assertions sont totalement déplacées. Je n'ai jamais vu de réel avantage en termes de performances, notamment en ce qui concerne la règle des 80/20.
Alors, suis-je passé à côté de l'essentiel, ou quelqu'un pourrait-il me dire pourquoi je devrais utiliser des assertions? Au fait, j'utilise des tests unitaires.
Premièrement, la différence de performance can est énorme. Dans un projet, nos affirmations ont littéralement provoqué un ralentissement 3x. Mais ils nous ont aidés à découvrir des insectes vraiment embêtants.
C'est exactement le point.
Les affirmations sont là pour vous aider à attraper les bugs. Et car ils sont supprimés dans les versions, nous pouvons nous permettre d’en mettre beaucoup sans se soucier des performances. Si vous n'êtes pas là pour agir sur des assertions manquantes, elles ne valent plus rien, alors nous ferions aussi bien de les supprimer.
Même détecter l'erreur et lancer une exception n'est pas vraiment une solution. La logique du programme est défectueuse et même si nous traitons l'exception, le programme est toujours en panne.
En gros, l’affirmation se résume comme suit: "Pourquoi ne pas prendre la peine d’attraper des erreurs que vous ne pouvez pas gérer?"
Certaines erreurs doivent être détectées pendant le développement. S'ils se faufilent devant les tests et dans la version de publication utilisée par un client, le programme est simplement interrompu et aucune vérification d'erreur d'exécution ne résoudra le problème.
Je n'ai jamais eu l'idée des assertions - pourquoi devriez-vous les utiliser?
Je veux dire, disons que j'étais un pilote de formule et que toutes les affirmations étaient des choses comme une ceinture de sécurité, un casque, etc.
Oui, c’est un bon exemple de cas où not utiliser une assertion. Ce sont des choses qui risquent de mal tourner au moment de l'exécution et qui doivent être vérifiées. Votre pilote de Formule 1 pourrait oublier certaines mesures de sécurité et s'il le fait, nous voulons tout arrêter avant que quiconque ne soit blessé.
Mais qu'en est-il de la vérification pour voir que le moteur est installé? Avons-nous besoin de vérifier que pendant la course ?
Bien sûr que non. Si nous entrons dans la course sans moteur, nous sommes foutus, et même si nous détectons l'erreur, il est trop tard pour faire quoi que ce soit.
Au lieu de cela, c'est une erreur qui doit être détectée pendant le développement ou pas du tout. Si les concepteurs oublient de mettre un moteur dans leur voiture, ils doivent détecter cette erreur lors du développement. C'est une affirmation. Cela concerne les développeurs pendant le développement, mais ensuite, l'erreur ne doit plus exister, et si c'est le cas, nous ne pouvons rien faire.
C'est fondamentalement la différence. Une exception est là pour aider l'utilisateur, en traitant les erreurs qui peuvent être traitées.
Une assertion est là pour vous aider vous , en vous avertissant des erreurs qui ne doivent jamais se produire, qui doivent être corrigées avant que le produit can soit expédié. Les erreurs qui ne dépendent pas de la saisie de l'utilisateur, mais du code qui fait ce qu'il est censé faire.
La racine carrée de quatre doit jamais évaluer à trois. L'erreur est tout simplement impossible. Si cela se produit, la logique de votre programme est simplement interrompue. Peu importe combien nous gérons le traitement des erreurs, c'est quelque chose qui doit être capturé pendant le développement ou pas du tout. Si nous avons utilisé la gestion des exceptions pour vérifier cette erreur et la gérer, que va faire l'exception? Dites à l'utilisateur "le programme est fondamentalement en panne. Ne l'utilisez jamais"?
Un email du développeur aurait pu atteindre cet objectif. Pourquoi se préoccuper de l'intégrer dans le code du programme? C'est un exemple de problème qui ne doit tout simplement pas se produire. Si c'est le cas, nous devons revenir en arrière et réparer le programme. Aucune autre forme de traitement d'erreur n'est possible.
Mais certaines erreurs, comme l'impossibilité d'ouvrir un fichier en lecture, sont possible . Même si cela peut être une mauvaise chose si cela se produit, nous devons accepter que cela peut se produire. Nous devons donc nous en occuper si cela se produit.
Les assertions servent à détecter les erreurs qui ne peuvent pas se produire.
Andrew Koenig avait jadis un { bonne discussion philosophique sur l'utilisation des exceptions et des assertions dans le code de transport) . En fin de compte, vous vous prémunissez contre toute agitation lorsque le programme est dans un état irrémédiablement cassé .
Je crois donc que, lorsqu'un programme découvre quelque chose qui ne va pas de façon irréfutable dans son état interne, il vaut mieux mettre fin immédiatement au programme plutôt que de laisser à son interlocuteur la possibilité de prétendre que rien ne va de travers.
Si vous voulez, je pense que les exceptions devraient être réservées aux situations dans lesquelles il est possible de faire quelque chose de sensé après avoir attrapé l'exception. Lorsque vous découvrez une condition que vous pensiez impossible, il est difficile de dire grand-chose sur ce qui pourrait arriver par la suite.
From Code Complete 2: "Utilisez le traitement des erreurs pour les conditions qui vous attendent, utilisez des assertions pour les conditions qui ne devraient jamais se produire."
Un exemple couramment cité est la vérification de zéro dans le dénominateur avant une division.
Vous êtes censé supprimer les assertions du code de production. Ils sont en cours de développement pour vous aider à détecter les erreurs.
Les tests unitaires ne remplacent pas les assertions.
Parce qu'ils facilitent le débogage.
La partie fastidieuse du débogage consiste à identifier un problème depuis le symptôme que vous avez remarqué en premier jusqu'à l'erreur dans le code. Des assertions bien écrites feront que le symptôme que vous remarquerez sera plus proche du problème du code actuel.
Un exemple très simple serait un bogue dans lequel vous indexez au-delà de la fin d'un tableau et causez une corruption de mémoire pouvant éventuellement provoquer un blocage. Cela peut prendre beaucoup de temps de remonter du crash à l'opération d'index incriminée. Toutefois, si vous avez une assertion à côté de cette opération d'indexation qui vérifie votre index, votre programme échouera juste à côté de l'erreur et vous pourrez ainsi trouver le problème rapidement.
C'est un sujet controversé. Beaucoup de gens, comme moi, préfèrent les laisser dans le code de production. Si votre programme va quand même aller dans les mauvaises herbes, vous pourriez aussi bien avoir l'affirmation dedans afin que votre client puisse au moins vous donner le numéro de ligne et le nom du fichier (ou toute autre information ou action que vous configurez pour faire l'assertion). Si vous avez omis l'affirmation, tout ce que le client pourrait vous signaler était "le crash est survenu".
Cela signifie que vous ne devriez probablement pas effectuer d'opérations coûteuses dans vos vérifications d'assertion, ou au moins profiler pour voir si elles vont causer des problèmes de performances.
Ils vous permettent de tester vos hypothèses. Par exemple, supposons que vous vouliez calculer la vitesse. Vous voudrez probablement affirmer que votre calcul est inférieur à la vitesse de la lumière.
Les assertions sont pour le développement, pour s'assurer que vous ne vous trompez pas.
D'après votre message, vous ne semblez pas être en désaccord avec l'idée d'utiliser des assertions, mais plutôt d'avoir des assertions dans le débogage et de ne pas les avoir actives dans la production.
La raison en est que lors du débogage, vous voudrez peut-être que le processus échoue de façon catastrophique - c’est-à-dire qu’il lève une exception et quitte, afin que l’erreur puisse être corrigée. En production, cela pourrait affecter l'ensemble de votre système et la condition d'erreur pourrait ne se produire que dans de très rares cas. Ainsi, en production, vous voudrez probablement enregistrer l'erreur, mais garder le processus en cours d'exécution.
L'utilisation d'assertions vous permet de modifier le comportement entre le débogage et la publication.
Je conviens avec vous que les assertions ne doivent pas simplement être réduites au silence dans le code de production: de nombreuses erreurs ne sont pas exposées dans les environnements de test et il est important de savoir quand les assertions échouent en production.
Les assertions sont inestimables lors de la refactorisation je pense. Si vous voulez remplacer alogrihm1 () par algorithmm2 (), vous pouvez les avoir tous les deux et affirmer que les résultats sont égaux. Vous pouvez ensuite éliminer progressivement algorithm1 ()
Les assertions sont également utiles pour certaines modifications que vous pourriez effectuer rapidement, mais ne sont pas trop sûres dans le contexte de l'état du système. Configurer des assertions pour les hypothèses que vous posez vous aiderait rapidement à signaler le problème, le cas échéant.
On peut se demander si les assertions doivent être supprimées en utilisant des macros ou autres, mais c'est ce qui a été fait dans les projets sur lesquels j'ai travaillé jusqu'à présent.
Dans Code complete est une section qui dit quelque chose comme. Chaque fois que vous écrivez un si sans aucun autre, il vous manque peut-être quelque chose.
C'est comme ce code
int i = 1
i = i++
Le programmeur commun ne pensera jamais à ce qui se passera si i est négatif dans le code ultérieur. Il y a une petite chance que votre code produise un débordement et que des langages comme Java vont de max int à min int et vous obtenez un très grand nombre négatif. Ce sont tous les cas que vous dites normalement. Euh ça n'arrivera jamais. Mais que fait votre programme si cela se produit? Donc, si vous savez qu'il y a quelque chose qui, selon vous, n'arrivera jamais, testez-le ou opposez-le et placez une affirmation fausse dans la clause else qui n'arrivera jamais au lieu de ne pas programmer la déclaration else. De cette façon, votre programme devrait complètement tomber en panne au moment où vous n'êtes plus sûr de ce qu'il fait. Dans le code de production, il devrait y avoir quelque chose de différent d’écraser quelque chose, comme informer l’utilisateur, le responsable, puis le quitter.
Une autre utilisation des assertions est la conception basée sur les contrats. Vous spécifiez un contrat avec votre interface et, en fonction de votre place dans le programme, vous affirmez votre entrée mais beaucoup plus important, vous affirmez votre sortie deux.
Je conviens avec vous que les assertions désactivées dans le code de production rendent les assertions plutôt inutiles. Et les assertions par défaut dans le cas de Java vm est un danger à mon avis.
Les assertions ne doivent être utilisées que pour vérifier les conditions pendant le développement qui ne sont pas nécessaires lors de la publication.
Voici un exemple très simple de la façon dont l'assertion peut être utilisée dans le développement.
A(char* p)
{
if(p == NULL)
throw exception;
B(p);
C(p);
}
B(char* p)
{
assert(p != NULL);
D(p);
do stuff;
}
C(char* p)
{
assert(p != NULL);
D(p);
do stuff;
}
D(char* p)
{
assert(p != NULL);
do stuff;
}
Au lieu d'appeler "if (p == NULL), déclenche une exception;" 5 fois, il suffit d'appeler une fois pour que vous sachiez que ce n'est pas NULL en entrant B (), C () et D (). Sinon, une assertion se fermera en phase de développement car vous avez "changé le code!" pas à cause d'une "entrée d'utilisateur".
Cela peut accélérer beaucoup l'exécution du code dans la version car il suffit d'appeler gcc avec "-DNDEBUG" pour que toutes les assertions ne soient pas compilées et que les "vérifications inutiles" soient supprimées de l'exécutable.
Dans de nombreux projets dans lesquels j'ai travaillé, les assertions ont été effectuées avec une macro personnalisée ayant un comportement différent dans Debug et Release.
Dans le débogage, si la condition est fausse, le débogueur est démarré à ce stade du code.
Dans Release, l'erreur est écrite dans un fichier journal, un avertissement est donné à l'utilisateur, puis le système tente de sauvegarder les données non sauvegardées. Être dans un état inconnu, cela peut échouer, mais cela vaut la peine d'essayer.
Je ne peux pas résister à citer "L'indispensable Calvin et Hobbes" p. 180:
Avant de descendre une pente raide comme celle-ci, il faut toujours vérifier la sécurité de son traîneau.
Droite.
Ceinture de sécurité ? Aucun.
Des signaux? Aucun.
Freins? Aucun.
Direction? Aucun.
WHEEEEEE
J'ai écrit du code dont les assertions ont une incidence sur les performances lorsqu'elles sont activées. Vérifier les conditions préalables et postérieures des fonctions mathématiques utilisées dans les boucles serrées par votre code graphique, par exemple (la fonction racine carrée met son résultat en carré et le compare à l'entrée, etc.). Bien sûr, c'est de l'ordre de quelques points de pourcentage, mais j'ai écrit du code qui nécessitait ces quelques points.
Plus important encore, j'ai écrit du code où les assertions faisaient une différence de plusieurs dizaines de points de pourcentage par rapport à la taille du code. Lorsque l'encombrement de la mémoire est un problème, les assertions dans le code de version sont probablement une extravagance inacceptable.
L'assertion doit être utilisée pour anticiper les erreurs dans la manière dont un programmeur utilise une API/fonction/classe/peu importe. Ces bogues doivent être corrigés rapidement au moment du débogage.
Pour tout le reste, jetez une exception.
Je l'utilise surtout pour des tests en cours de développement. Par exemple, voici le test de fumée de ma bibliothèque utf-8 Chaque fois que je modifie le code de la bibliothèque, je lance les tests et si un bogue est introduit, une assertion se déclenche. Bien sûr, j'aurais pu utiliser un cadre de test unitaire complet, mais pour ce qui est de mon propos, les affirmations sont bonnes.
L'assertion doit être utilisée lorsque vous faites quelque chose comme ça
a = set()
a.add('hello')
assert 'hello' in a
ou
a = 1;
assert a == 1; // if ram corruption happened and flipped the bit, this is the time to assert
En ce qui concerne les exceptions, vous traitez par programme:
while True:
try:
data = open('sample.file').read()
break // successfully read
except IOError:
// disk read fail from time to time.. so retry
pass
La plupart du temps, il est préférable de redémarrer votre application lorsque l'assertion survient, car vous ne voulez pas traiter les cas impossibles. Mais lorsque le cas attendu se produit (erreurs attendues (la plupart du temps des clients de la boîte noire, appels réseau, etc.), les exceptions doivent être utilisées.