Utilisation efficace du bloc try / catch?

21

Les blocs catch doivent-ils être utilisés pour écrire la logique, c'est-à-dire gérer le contrôle de flux, etc.? Ou tout simplement pour lever des exceptions? Cela affecte-t-il l'efficacité ou la maintenabilité du code?

Quels sont les effets secondaires (s'il y en a) de l'écriture de la logique dans le bloc catch?

ÉDITER:

J'ai vu une classe Java SDK dans laquelle ils ont écrit la logique à l'intérieur du bloc catch. Par exemple (extrait de java.lang.Integerclasse):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Je suivais un tutoriel où ils le considèrent comme un avantage d'écrire la logique des cas exceptionnels à l'intérieur des exceptions:

Les exceptions vous permettent d'écrire le flux principal de votre code et de traiter les cas exceptionnels ailleurs.

Des directives spécifiques quand écrire la logique dans le bloc catch et quand ne pas le faire?

HashimR
la source
12
@Coder Je pense que vous généralisez un peu trop. D'autant plus qu'avec de nombreuses API existantes, vous ne pouvez pas l'éviter.
CodesInChaos
8
@Coder: En Java, les exceptions sont souvent utilisées comme mécanisme de signalisation des problèmes qui font partie du flux de code légitime. Par exemple, si vous essayez d'ouvrir un fichier et qu'il échoue, la bibliothèque Java vous le dit en lançant une exception, car les API qui mènent à l'ouverture d'un fichier n'ont aucun autre mécanisme pour renvoyer une erreur.
JeremyP
4
@Coder dépend. Je pense que lorsque je fais des E / S ou lors de l'analyse de données complexes, qui sont presque toujours correctement formatées, lever une exception pour signifier une erreur rend le code beaucoup plus propre.
CodesInChaos
1
@Coded Oui, l'exception est là pour que la méthode vous dise qu'elle n'a pas pu faire ce qui lui a été demandé. Mais les exceptions ne doivent pas être utilisées pour le contrôle de flux, c'est pourquoi C # a également une méthode int.TryParse qui ne lance pas.
Andy
1
@Coder: C'est de la foutaise. Les choses qui sont exceptionnelles à un niveau de code peuvent ne pas l'être à un autre - ou vous pouvez, par exemple, présenter ou ajouter des informations d'erreur ou de débogage avant de continuer.
DeadMG

Réponses:

46

L'exemple que vous citez est dû à une mauvaise conception de l'API (il n'existe aucun moyen propre de vérifier si une chaîne est un entier valide, sauf en essayant de l'analyser et en interceptant l'exception).

Au niveau technique, throw et try / catch sont des constructions de flux de contrôle qui vous permettent de sauter la pile des appels, rien de plus et rien de moins. Le fait de sauter la pile d'appels connecte implicitement du code qui n'est pas rapproché dans la source, ce qui est mauvais pour la maintenabilité . Il ne doit donc être utilisé que lorsque vous en avez besoin et les alternatives sont encore pires. Le cas largement accepté où les alternatives sont pires est la gestion des erreurs (codes de retour spéciaux qui doivent être vérifiés et transmis manuellement à chaque niveau de la pile d'appels).

Si vous avez un cas où les alternatives sont pires (et que vous les avez vraiment examinées attentivement), alors je dirais que lancer et essayer / attraper pour le flux de contrôle est correct. Le dogme n'est pas un bon substitut au jugement.

Michael Borgwardt
la source
1
+1, bons points faits. Je pense que certains traitements peuvent être valides dans la capture tels que la préparation d'un message d'erreur et la journalisation des erreurs. La libération de ressources coûteuses telles que les connexions db et les références COM (.NET) pourrait être ajoutée au bloc Final.
NoChance
@EmmadKareem J'ai pensé à libérer des ressources, mais vous devez généralement les libérer de toute façon à un moment donné, même sans situation d'erreur. Ainsi, vous pourriez vous répéter. La préparation d'un message de journal est bien sûr acceptable.
scarfridge
@MichaelBorgwardt Je pense que la conception de l'API n'est pas si pauvre (bien qu'elle puisse être meilleure). Essayer d'analyser une chaîne invalide est clairement une condition d'erreur et donc exactement le "cas largement accepté" que vous avez mentionné. D'accord, une méthode pour déterminer si une chaîne est syntaxiquement correcte est utile (c'est là que cela pourrait être mieux). Cependant, forcer tout le monde à appeler cette méthode avant l'analyse réelle n'est pas possible et nous devons gérer l'erreur. Mélanger le résultat réel et le code d'erreur est inconfortable et vous lèveriez donc toujours l'exception. Mais peut-être une exception d'exécution.
scarfridge
3
+1 pour cette dernière phrase.
FrustratedWithFormsDesigner
2
@scarfridge: Je suis entièrement d'accord avec vous :) L'absence d'une méthode isInteger (String) à retour booléen est tout ce que je voulais dire avec "une mauvaise conception de l'API"
Michael Borgwardt
9

C'est quelque chose qui a tendance à dépendre du langage et du paradigme.

La plupart de mon travail se fait en Java (et parfois en C ++). La tendance est de n'utiliser les exceptions que pour des conditions exceptionnelles. Il y a quelques questions sur Stack Overflow sur la surcharge de performances des exceptions en Java , et comme vous pouvez le voir, ce n'est pas exactement négligeable. Il existe également d'autres préoccupations, telles que la lisibilité et la maintenabilité du code. Lorsqu'elles sont utilisées correctement, les exceptions peuvent déléguer la gestion des erreurs aux composants appropriés.

Cependant, en Python, l'idée qu'il est plus facile de demander pardon que la permission règne. Dans la communauté Python, cela est connu sous le nom d' EAFP , qui contraste avec l' approche "regardez avant de sauter" ( LBYL ) dans les langages de style C.

Thomas Owens
la source
2
+1 pour avoir mentionné les frais généraux avec des exceptions. Je fais .NET et (au moins sur ma machine) je pourrais même sentir quand une exception est sur le point de se produire par la durée que cela prend dans certains cas par rapport à d'autres opérations normales.
NoChance
2
@EmmadKareem Uniquement si vous avez un débogueur attaché. Vous pouvez en lancer plusieurs milliers par seconde sans débogueur.
CodesInChaos
@CodeInChaos, vous avez raison. Comment pourrais-je savoir, j'écris un code si propre qui n'en obtient jamais au moment de l'exécution :)
NoChance
@EmmadKareem Selon la façon dont on écrit une application réseau, les échecs de connexion ou de protocole sont traités avec une exception. Les exceptions doivent être raisonnablement rapides dans une telle application pour éviter les attaques DoS triviales.
CodesInChaos
@CodeInChaos, c'est bon à savoir. Je plaisantais dans mon dernier commentaire.
NoChance
9

Ce n'est pas la meilleure façon de penser aux blocs try / catch. La façon de penser aux blocs try / catch est la suivante: un échec s'est produit, où est le meilleur endroit pour faire face à CET échec spécifique. Cela peut être la ligne de code la plus proche, elle peut se situer à vingt niveaux dans la chaîne d'appel. Où qu'il soit, c'est là qu'il devrait être.

Ce que fait le bloc catch dépend de l'erreur et de ce que vous pouvez y faire. Parfois, il peut simplement être ignoré (échec de la suppression d'un fichier de travail sans données importantes), parfois il sera utilisé pour définir la valeur de retour d'une fonction sur vrai ou faux (méthode d'analyse par int de java), parfois vous quitterez le programme . Tout cela dépend de l'erreur.

La partie importante à comprendre est la suivante: catch block == Je sais comment y faire face.

jmoreno
la source
6

Les blocs catch ne doivent être utilisés que pour gérer les exceptions, rien d'autre et jamais pour le flux de contrôle. Dans toute situation où vous souhaitez utiliser des exceptions pour gérer le flux, vous feriez mieux de vérifier les conditions préalables.

Le traitement du flux avec des exceptions est lent et sémantiquement incorrect.

Tom Squires
la source
4
Je sais ce que vous dites, mais il convient de noter que tout ce que font les exceptions est de gérer le flux de programme - juste qu'il ne doit être utilisé que pour contrôler le flux d'erreur exceptionnel ...
Max
N'y a-t-il pas de directives spécifiques quand écrire la logique dans le bloc catch et quand ne pas le faire?
HashimR
4
Les analyseurs numériques et les formateurs de texte de Java sont des exemples célèbres d'utilisation problématique des exceptions: la seule façon raisonnable de vérifier une condition préalable (qu'une chaîne représente un nombre analysable) est d'essayer de l'analyser, et si elle échoue avec une exception, quels sont vos choix en pratique? Vous devez intercepter l'exception et gérer le flux avec elle, peu importe qu'elle soit considérée sémantiquement incorrecte.
Joonas Pulakka
@JoonasPulakka Je pense que votre commentaire par rapport aux analyseurs de nombres et aux formateurs de texte est considéré comme une réponse complète à cette question
moucher
5

Les blocs catch doivent-ils être utilisés pour écrire la logique, c'est-à-dire gérer le contrôle de flux, etc.? Ou tout simplement pour lever des exceptions? Cela affecte-t-il l'efficacité ou la maintenabilité du code?

Tout d'abord, oubliez la notion selon laquelle "les exceptions doivent être utilisées pour des conditions exceptionnelles". Arrêtez également de vous soucier de l'efficacité, jusqu'à ce que vous ayez un code qui fonctionne de manière inacceptable et que vous ayez mesuré pour savoir où se situe le problème.

Le code est plus facile à comprendre lorsque les actions suivent dans une séquence simple, sans conditions. Les exceptions améliorent la maintenabilité en supprimant la vérification des erreurs du flux normal. Peu importe que l'exécution suive le flux normal 99,9% du temps ou 50% du temps ou 20% du temps.

Lance une exception lorsqu'une fonction n'est pas en mesure de renvoyer une valeur, lorsqu'une procédure n'est pas en mesure de terminer l'action attendue ou lorsqu'un constructeur est incapable de produire un objet utilisable. Cela permet aux programmeurs de supposer qu'une fonction renvoie toujours un résultat utilisable, une procédure termine toujours l'action demandée, un objet construit est toujours dans un état utilisable.

Le plus gros problème que je vois avec le code de gestion des exceptions est que les programmeurs écrivent des blocs try / catch alors qu'ils ne devraient pas. Par exemple, dans la plupart des applications Web, si une demande de base de données lève une exception, rien ne peut être fait dans le contrôleur. Une clause de capture générique au plus haut niveau suffit. Ensuite, le code du contrôleur peut parfaitement ignorer la possibilité que le disque soit tombé en panne ou que la base de données soit hors ligne ou autre.

Kevin Cline
la source
1

Les blocs de capture ne doivent pas être utilisés pour écrire la logique du code. Ils ne doivent être utilisés que pour gérer les erreurs. Un exemple est (1) nettoyer toutes les ressources allouées, (2) imprimer un message utile, et (3) quitter normalement.

Il est vrai que les exceptions peuvent être abusées et provoquer des gotoeffets indésirables . Mais cela ne les rend pas inutiles. Lorsqu'ils sont correctement utilisés , ils peuvent améliorer de nombreux aspects de votre code.

sakisk
la source