Je ne peux pas décider comment gérer les exceptions dans mon application.
Beaucoup si mes problèmes avec des exceptions viennent 1) de l'accès aux données via un service distant ou 2) de la désérialisation d'un objet JSON. Malheureusement, je ne peux garantir le succès de l'une ou l'autre de ces tâches (coupure de la connexion réseau, objet JSON mal formé qui est hors de mon contrôle).
En conséquence, si je rencontre une exception, je l'attrape simplement dans la fonction et renvoie FALSE à l'appelant. Ma logique est que tout ce qui importe vraiment à l'appelant est de savoir si la tâche a réussi, pas pourquoi elle n'a pas réussi.
Voici un exemple de code (en JAVA) d'une méthode typique)
public boolean doSomething(Object p_somthingToDoOn)
{
boolean result = false;
try{
// if dirty object then clean
doactualStuffOnObject(p_jsonObject);
//assume success (no exception thrown)
result = true;
}
catch(Exception Ex)
{
//don't care about exceptions
Ex.printStackTrace();
}
return result;
}
Je pense que cette approche est bonne, mais je suis vraiment curieux de savoir quelles sont les meilleures pratiques pour gérer les exceptions (devrais-je vraiment faire apparaître une exception tout au long d'une pile d'appels?).
En résumé des questions clés:
- Est-il acceptable de simplement attraper des exceptions mais de ne pas les faire apparaître ou de notifier formellement le système (via un journal ou une notification à l'utilisateur)?
- Quelles sont les meilleures pratiques pour les exceptions qui n'entraînent pas que tout nécessite un bloc try / catch?
Suivi / Modifier
Merci pour tous vos commentaires, nous avons trouvé d'excellentes sources sur la gestion des exceptions en ligne:
- Meilleures pratiques pour la gestion des exceptions | O'Reilly Media
- Bonnes pratiques de gestion des exceptions dans .NET
- Meilleures pratiques: gestion des exceptions (l'article pointe maintenant vers la copie d'archive.org)
- Antipatterns à gestion d'exception
Il semble que la gestion des exceptions est l'une de ces choses qui varient en fonction du contexte. Mais surtout, il faut être cohérent dans la façon dont ils gèrent les exceptions au sein d'un système.
De plus, faites attention à la pourriture du code via des essais / captures excessifs ou en ne respectant pas une exception (une exception avertit le système, que faut-il encore avertir?).
En outre, c'est un joli commentaire de choix de m3rLinEz .
J'ai tendance à être d'accord avec Anders Hejlsberg et vous que la plupart des appelants ne se soucient que de savoir si l'opération réussit ou non.
À partir de ce commentaire, il soulève quelques questions à prendre en compte lors du traitement des exceptions:
- À quel point cette exception est-elle lancée?
- Comment est-il logique de le gérer?
- L'appelant se soucie-t-il vraiment de l'exception ou se soucie-t-il simplement de la réussite de l'appel?
- Le fait de forcer un appelant à gérer une exception potentielle est-il gracieux?
- Êtes-vous respectueux des idomes de la langue?
- Avez-vous vraiment besoin de renvoyer un indicateur de succès comme booléen? Renvoyer un booléen (ou un int) est plus un état d'esprit C qu'un Java (en Java, vous ne feriez que gérer l'exception).
- Suivez les constructions de gestion des erreurs associées à la langue :)!
la source
Réponses:
Il me semble étrange que vous vouliez attraper des exceptions et les transformer en codes d'erreur. Pourquoi pensez-vous que l'appelant préférerait les codes d'erreur aux exceptions lorsque cette dernière est la valeur par défaut à la fois en Java et en C #?
Quant à vos questions:
la source
Cela dépend de l'application et de la situation. Si vous créez un composant de bibliothèque, vous devez faire apparaître des exceptions, même si elles doivent être encapsulées pour être contextuelles avec votre composant. Par exemple, si vous créez une base de données Xml et disons que vous utilisez le système de fichiers pour stocker vos données, et que vous utilisez les autorisations du système de fichiers pour sécuriser les données. Vous ne voudriez pas faire apparaître une exception FileIOAccessDenied car cela fuit votre implémentation. Au lieu de cela, vous encapsuleriez l'exception et lèveriez une erreur AccessDenied. Cela est particulièrement vrai si vous distribuez le composant à des tiers.
Quant à savoir s'il est normal d'avaler des exceptions. Cela dépend de votre système. Si votre application peut gérer les cas d'échec et qu'il n'y a aucun avantage à informer l'utilisateur pourquoi elle a échoué, continuez, bien que je vous recommande fortement de consigner l'échec. J'ai toujours trouvé frustrant d'être appelé pour aider à résoudre un problème et constater qu'ils avalaient l'exception (ou la remplaçaient et en lançaient une nouvelle à la place sans définir l'exception interne).
En général, j'utilise les règles suivantes:
Je trouve que le code suivant est une odeur:
Un code comme celui-ci ne sert à rien et ne doit pas être inclus.
la source
// do something
inclut destry/finally
blocs autour du point qui jette, lesfinally
blocs s'exécuteront avant lecatch
bloc. Sans letry/catch
, l'exception volera jusqu'au sommet de la pile sans qu'aucunfinally
bloc ne soit exécuté. Cela permet au gestionnaire de niveau supérieur de décider d'exécuter ou non lesfinally
blocs.Je voudrais recommander une autre bonne source sur le sujet. C'est une interview avec les inventeurs de C # et Java, Anders Hejlsberg et James Gosling respectivement, sur le thème de l'exception vérifiée de Java.
Échec et exceptions
Il y a aussi d'excellentes ressources au bas de la page.
J'ai tendance à être d'accord avec Anders Hejlsberg et vous que la plupart des appelants ne se soucient que de savoir si l'opération réussit ou non.
EDIT: Ajout de plus de détails sur la converstaion
la source
FetchData
et qu'il lève une exception d'un type inattendu, il n'a aucun moyen de savoir si cette exception signifie simplement que les données sont indisponibles (auquel cas la capacité du code à s'en sortir sans elle la «résoudrait»), ou si cela signifie que le CPU est en feu et que le système doit effectuer un «arrêt de sécurité» à la première occasion. On dirait que M. Hejlsberg suggère que le code devrait assumer le premier; c'est peut-être la meilleure stratégie possible étant donné les hiérarchies d'exceptions existantes, mais cela semble néanmoins dégoûtant.Les exceptions cochées sont un problème controversé en général, et en Java en particulier (plus tard, je vais essayer de trouver quelques exemples pour ceux qui sont pour et qui s'y opposent).
En règle générale, la gestion des exceptions doit être quelque chose autour de ces directives, sans ordre particulier:
printStackTrace()
ou quelque chose de semblable, il y a de fortes chances que l'un de vos utilisateurs finisse par obtenir l'une de ces traces de pile et qu'il ne sache absolument pas quoi en faire.Exception
, vous êtes très susceptible d'avaler des exceptions autrement importantes.Error
s !! , ce qui signifie: Ne jamais attraperThrowable
s car lesError
s sont des sous-classes de ces derniers.Error
s sont des problèmes que vous ne pourrez probablement jamais gérer (par exempleOutOfMemory
, ou d'autres problèmes de JVM)Concernant votre cas spécifique, assurez-vous que tout client appelant votre méthode recevra la valeur de retour appropriée. Si quelque chose échoue, une méthode de retour booléen peut retourner false, mais assurez-vous que les endroits que vous appelez cette méthode sont capables de gérer cela.
la source
Vous ne devriez attraper que les exceptions que vous pouvez gérer. Par exemple, si vous avez affaire à la lecture sur un réseau et que la connexion expire et que vous obtenez une exception, vous pouvez réessayer. Cependant, si vous lisez sur un réseau et obtenez une exception IndexOutOfBounds, vous ne pouvez vraiment pas gérer cela car vous ne savez pas (enfin, dans ce cas, vous ne saurez pas) ce qui l'a causé. Si vous allez renvoyer false ou -1 ou null, assurez-vous que c'est pour des exceptions spécifiques. Je ne veux pas qu'une bibliothèque que j'utilise renvoie un faux sur une lecture réseau lorsque l'exception levée est que le tas est à court de mémoire.
la source
Les exceptions sont des erreurs qui ne font pas partie de l'exécution normale du programme. En fonction de ce que fait votre programme et de ses utilisations (c'est-à-dire un traitement de texte par rapport à un moniteur cardiaque), vous voudrez faire des choses différentes lorsque vous rencontrez une exception. J'ai travaillé avec du code qui utilise des exceptions dans le cadre de l'exécution normale et c'est définitivement une odeur de code.
Ex.
Ce code me rend barf. OMI, vous ne devriez pas récupérer des exceptions à moins que ce soit un programme critique. Si vous lancez des exceptions, de mauvaises choses se produisent.
la source
Tout ce qui précède semble raisonnable et votre lieu de travail peut souvent avoir une politique. Chez nous, nous avons défini les types d'exception:
SystemException
(non cochée) etApplicationException
(cochée).Nous avons convenu qu'il
SystemException
est peu probable que les s soient récupérables et qu'ils seront traités une fois au sommet. Fournir davantage de contexte, nosSystemException
s sont exteneded pour indiquer où ils se sont produits, par exempleRepositoryException
,ServiceEception
etc.ApplicationException
Les s peuvent avoir une signification commerciale commeInsufficientFundsException
et doivent être gérées par le code client.Sans un exemple concret, il est difficile de commenter votre implémentation, mais je n'utiliserais jamais de codes de retour, c'est un problème de maintenance. Vous pouvez avaler une exception, mais vous devez décider pourquoi et toujours consigner l'événement et le stacktrace. Enfin, comme votre méthode n'a pas d'autre traitement, elle est assez redondante (sauf pour l'encapsulation?), Donc
doactualStuffOnObject(p_jsonObject);
pourrait retourner un booléen!la source
Après avoir réfléchi et examiné votre code, il me semble que vous relancez simplement l'exception en tant que booléen. Vous pouvez simplement laisser la méthode passer cette exception (vous n'avez même pas besoin de l'attraper) et la gérer dans l'appelant, car c'est là que cela compte. Si l'exception oblige l'appelant à réessayer cette fonction, l'appelant doit être celui qui intercepte l'exception.
Il peut parfois arriver que l'exception que vous rencontrez n'ait pas de sens pour l'appelant (c'est-à-dire qu'il s'agit d'une exception réseau), auquel cas vous devez l'envelopper dans une exception spécifique au domaine.
Si d'un autre côté, l'exception signale une erreur irrécupérable dans votre programme (c'est-à-dire que le résultat éventuel de cette exception sera la fin du programme), j'aime personnellement rendre cela explicite en l'attrapant et en lançant une exception d'exécution.
la source
Si vous comptez utiliser le modèle de code dans votre exemple, appelez-le TryDoSomething et interceptez uniquement des exceptions spécifiques.
Pensez également à utiliser un filtre d'exception lors de la journalisation des exceptions à des fins de diagnostic. VB prend en charge la langue pour les filtres d'exception. Le lien vers le blog de Greggm a une implémentation qui peut être utilisée à partir de C #. Les filtres d'exception ont de meilleures propriétés de débogage par rapport à la capture et à la relance. Plus précisément, vous pouvez enregistrer le problème dans le filtre et laisser l'exception continuer à se propager. Cette méthode permet à un débogueur JIT (Just in Time) de joindre la pile d'origine complète. Une relance coupe la pile au moment où elle a été relancée.
Les cas où TryXXXX a du sens sont lorsque vous encapsulez une fonction tierce qui lance des cas qui ne sont pas vraiment exceptionnels ou qui sont simples et difficiles à tester sans appeler la fonction. Un exemple serait quelque chose comme:
Que vous utilisiez ou non un modèle comme TryXXX est plus une question de style. La question de saisir toutes les exceptions et de les avaler n'est pas une question de style. Assurez-vous que les exceptions inattendues sont autorisées à se propager!
la source
Je suggère de vous inspirer de la bibliothèque standard pour la langue que vous utilisez. Je ne peux pas parler pour C #, mais regardons Java.
Par exemple, java.lang.reflect.Array a une
set
méthode statique :La voie C serait
... avec la valeur de retour étant un indicateur de succès. Mais vous n'êtes plus dans le monde C.
Une fois que vous avez adopté les exceptions, vous devriez constater que cela rend votre code plus simple et plus clair, en éloignant votre code de gestion des erreurs de votre logique de base. Essayez d'avoir beaucoup d'instructions dans un seul
try
bloc.Comme d'autres l'ont noté, vous devez être aussi précis que possible dans le type d'exception que vous détectez.
la source
Si vous allez intercepter une exception et renvoyer false, cela devrait être une exception très spécifique. Vous ne faites pas cela, vous les attrapez tous et vous retournez faux. Si j'obtiens une MyCarIsOnFireException, je veux en savoir plus tout de suite! Le reste des exceptions dont je ne me soucie peut-être pas. Vous devriez donc avoir une pile de gestionnaires d'exceptions qui disent "whoa whoa quelque chose ne va pas ici" pour certaines exceptions (relancez, ou attrapez et relancez une nouvelle exception qui explique mieux ce qui s'est passé) et renvoyez simplement false pour d'autres.
S'il s'agit d'un produit que vous allez lancer, vous devriez enregistrer ces exceptions quelque part, cela vous aidera à peaufiner les choses à l'avenir.
Edit: Quant à la question de tout emballer dans un try / catch, je pense que la réponse est oui. Les exceptions doivent être si rares dans votre code que le code du bloc catch s'exécute si rarement qu'il n'atteint pas du tout les performances. Une exception devrait être un état dans lequel votre machine d'état est tombée en panne et ne sait pas quoi faire. Au moins, renvoyez une exception qui explique ce qui se passait à ce moment-là et contient l'exception interceptée. «Exception dans la méthode doSomeStuff ()» n'est pas très utile pour quiconque doit comprendre pourquoi il s'est cassé pendant que vous êtes en vacances (ou à un nouvel emploi).
la source
Ma stratégie:
Si la fonction d'origine a renvoyé void, je la change pour retourner bool . Si une exception / erreur s'est produite, retournez false , si tout va bien, retournez true .
Si la fonction doit retourner quelque chose, quand une exception / erreur s'est produite, renvoie null , sinon l'élément retournable.
Au lieu de booléen, une chaîne peut être renvoyée contenant la description de l'erreur.
Dans tous les cas, avant de retourner quoi que ce soit, enregistrez l'erreur.
la source
Quelques excellentes réponses ici. Je voudrais ajouter que si vous vous retrouvez avec quelque chose comme vous l'avez posté, imprimez au moins plus que la trace de pile. Dites ce que vous faisiez à l'époque, et Ex.getMessage (), pour donner au développeur une chance de se battre.
la source
Les blocs try / catch forment un deuxième ensemble de logique intégré au premier ensemble (principal), en tant que tels, ils sont un excellent moyen de marteler du code spaghetti illisible et difficile à déboguer.
Pourtant, utilisés raisonnablement, ils fonctionnent à merveille en termes de lisibilité, mais vous devez simplement suivre deux règles simples:
utilisez-les (avec parcimonie) au bas niveau pour attraper les problèmes de gestion de la bibliothèque et les renvoyer dans le flux logique principal. La plupart de la gestion des erreurs que nous souhaitons doit provenir du code lui-même, dans le cadre des données elles-mêmes. Pourquoi faire des conditions spéciales, si les données renvoyées ne sont pas spéciales?
utilisez un gros gestionnaire au niveau supérieur pour gérer tout ou partie des conditions étranges qui se produisent dans le code et qui ne sont pas capturées à un niveau bas. Faites quelque chose d'utile avec les erreurs (journaux, redémarrages, récupérations, etc.).
En dehors de ces deux types de gestion des erreurs, tout le reste du code au milieu doit être libre et exempt de code try / catch et d'objets d'erreur. De cette façon, il fonctionne simplement et comme prévu, peu importe où vous l'utilisez ou ce que vous en faites.
Paul.
la source
Je suis peut-être un peu en retard avec la réponse, mais la gestion des erreurs est quelque chose que nous pouvons toujours changer et évoluer avec le temps. Si vous voulez en savoir plus sur ce sujet, j'ai écrit un article à ce sujet dans mon nouveau blog. http://taoofdevelopment.wordpress.com
Bon codage.
la source