117 votes

Meilleures pratiques pour la gestion des exceptions en Java ou C#

Je suis coincé pour décider comment gérer les exceptions dans mon application.

La plupart de mes problèmes avec les exceptions proviennent 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 pas garantir le succès de l'une ou l'autre de ces tâches (connexion réseau coupée, objet JSON malformé qui échappe à mon contrôle).

Par conséquent, si je rencontre une exception, je l'attrape simplement dans la fonction et renvoie FALSE à l'appelant. Ma logique est que tout ce qui intéresse vraiment l'appelant est de savoir si la tâche a réussi, et non 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 en matière de gestion des exceptions (dois-je vraiment faire bouillonner une exception tout au long de la pile d'appels ?)

En résumé des questions clés :

  1. Est-il possible de se contenter d'attraper les exceptions sans les faire apparaître sous forme de bulles ou de notifier formellement le système (via un journal ou une notification à l'utilisateur) ?
  2. Quelles sont les meilleures pratiques pour les exceptions qui n'entraînent pas la nécessité d'un bloc try/catch ?

Suivi/Modification

Merci pour tous les commentaires, j'ai trouvé d'excellentes sources sur la gestion des exceptions en ligne :

Il semble que la gestion des exceptions soit une de ces choses qui varient en fonction du contexte. Mais le plus important, c'est qu'il faut être cohérent dans la façon dont on gère les exceptions dans un système.

En outre, faites attention à la pourriture du code par le biais d'un nombre excessif de try/catches ou en ne donnant pas à une exception le respect qui lui est dû (une exception avertit le système, qu'y a-t-il d'autre à avertir ?).

De plus, c'est un commentaire plutôt judicieux de la part de m3rLinEz .

J'ai tendance à être d'accord avec Anders Hejlsberg et vous que la plupart des appelants ne se que de savoir si l'opération est réussie ou non.

Ce commentaire soulève quelques questions auxquelles il faut réfléchir lorsqu'on traite des exceptions :

  • Quel est l'intérêt de la levée de cette exception ?
  • Comment faut-il s'y prendre ?
  • L'appelant se soucie-t-il vraiment de l'exception ou simplement du fait que l'appel a réussi ?
  • Est-ce que forcer un appelant à gérer une exception potentielle est gracieux ?
  • Êtes-vous respectueux des idomes de la langue ?
    • Avez-vous vraiment besoin de renvoyer un indicateur de réussite comme un booléen ? Le retour d'un booléen (ou d'un int) est plus un état d'esprit C que Java (en Java, vous vous contenteriez de gérer l'exception).
    • Suivez les constructions de gestion des erreurs associées à la langue :) !

61voto

Brian Rasmussen Points 68853

Il me semble étrange que vous vouliez attraper les exceptions et les transformer en codes d'erreur. Pourquoi pensez-vous que l'appelant préférerait les codes d'erreur aux exceptions alors que ces dernières sont par défaut en Java et en C# ?

Quant à vos questions :

  1. Vous ne devriez attraper que les exceptions que vous pouvez réellement gérer. Juste attraper des exceptions n'est pas la bonne chose à faire dans la plupart des cas. Il existe quelques exceptions (par exemple, la journalisation et le marshalling des exceptions entre les threads), mais même dans ces cas-là, vous devez généralement relancer les exceptions.
  2. Vous ne devriez certainement pas avoir beaucoup d'instructions try/catch dans votre code. Encore une fois, l'idée est d'attraper uniquement les exceptions que vous pouvez gérer. Vous pouvez inclure un gestionnaire d'exception supérieur pour transformer toute exception non gérée en quelque chose d'un peu utile pour l'utilisateur final. exceptions non gérées en quelque chose d'utile pour l'utilisateur final mais mais sinon, vous ne devez pas essayer d'attraper toutes les exceptions de chaque exception à tous les endroits possibles.

25voto

JoshBerke Points 34238

Cela dépend de l'application et de la situation. Si vous construisez un composant de bibliothèque, vous devez faire apparaître les exceptions dans une bulle, bien qu'elles doivent être enveloppées pour être contextuelles à votre composant. Par exemple, si vous construisez une base de données Xml, 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 surgir une exception FileIOAccessDenied, car cela mettrait à mal votre implémentation. Vous devriez plutôt envelopper l'exception et lancer une erreur AccessDenied. Cela est particulièrement vrai si vous distribuez le composant à des tiers.

Quant à savoir si c'est bien 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 de la raison de l'échec, alors allez-y, même si je vous recommande fortement d'enregistrer l'échec. J'ai toujours trouvé frustrant d'être appelé pour aider à résoudre un problème et de découvrir qu'ils avalent l'exception (ou la remplacent et en lancent une nouvelle à la place sans définir l'exception interne).

En général, j'utilise les règles suivantes :

  1. Dans mes composants et bibliothèques, je n'attrape une exception que si j'ai l'intention de la traiter ou de faire quelque chose à partir d'elle. Ou si je veux fournir des informations contextuelles supplémentaires dans une exception.
  2. J'utilise un try catch général au point d'entrée de l'application, ou au niveau le plus élevé possible. Si une exception survient, je l'enregistre et je la laisse échouer. Idéalement, les exceptions ne devraient jamais arriver ici.

Je trouve que le code suivant est une odeur :

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Un code comme celui-ci ne sert à rien et ne devrait pas être inclus.

9voto

Gant Points 9920

J'aimerais vous recommander une autre bonne source sur le sujet. Il s'agit d'une interview des 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

Vous trouverez également d'excellentes ressources en bas de la page.

J'ai tendance à être d'accord avec Anders Hejlsberg et vous sur le fait que la plupart des appelants ne se soucient que de savoir si l'opération est réussie ou non.

Bill Venners : Vous avez mentionné des problèmes d'évolutivité et de versionnage en ce qui concerne les exceptions vérifiées. Pourriez-vous préciser ce que vous entendez par ces deux problèmes ?

Anders Hejlsberg : Commençons par le versioning, parce que les problèmes sont assez faciles à voir. Disons que je crée une méthode foo qui déclare qu'elle lance les exceptions A, B, et C. Dans la version deux de foo, je veux ajouter un un tas de fonctionnalités, et maintenant foo pourrait lancer l'exception D. C'est un changement changement pour moi d'ajouter D à la clause throws de cette méthode, car l'appelant actuel de cette méthode ne presque certainement pas gérer cette exception.

L'ajout d'une nouvelle exception à une clause throws dans une nouvelle version casse le code du client code. C'est comme ajouter une méthode à une interface. Après avoir publié une interface, elle est à toutes fins pratiques immuable, parce que n'importe quelle implémentation de l'interface peut avoir les méthodes que vous souhaitez ajouter dans la prochaine version. Donc vous devez créer une nouvelle interface à la place. De même, avec les exceptions, vous auriez soit créer une toute nouvelle méthode appelée foo2 qui lève d'autres exceptions, soit vous devriez attraper l'exception D dans dans la nouvelle méthode foo, et transformer le D en un A, un B ou un C.

Bill Venners : Mais ne brisez-vous pas leur code dans ce cas de toute façon, même dans un langage sans exceptions exceptions vérifiées ? Si la nouvelle version de foo va lever une nouvelle exception que les clients devraient penser à la gérer, leur code n'est-il pas cassé par le simple fait le fait qu'ils ne s'attendaient pas à cette exception quand ils ont écrit le code ?

Anders Hejlsberg : Non, parce que dans beaucoup de cas, les gens s'en fichent. Ils ne vont ne vont pas gérer ces exceptions. Il y a un niveau inférieur de traitement des exceptions autour de leur message de message. Ce gestionnaire va juste faire apparaître un dialogue qui dit ce qui s'est mal et continuer. Les programmeurs protègent leur code en écrivant try et des "finally" partout, pour qu'ils puissent faire marche arrière correctement si une exception se produit, mais ils ne sont pas vraiment intéressés à gérer les exceptions.

La clause throws, du moins la façon dont est implémentée en Java, ne vous oblige pas vous oblige pas nécessairement à gérer les exceptions, mais si vous ne les traitez pas elles, elle vous oblige à reconnaître précisément quelles exceptions peuvent passer passer. Cela vous oblige soit à attraper les exceptions déclarées ou les mettre dans votre propre clause throws. Pour contourner autour de cette exigence, les gens font des choses ridicules. Par exemple, ils décorent chaque méthode avec "throws Exception." Cela va complètement complètement la fonctionnalité, et vous venez de faire le programmeur à écrire plus de gunk. Cela n'aide personne.

EDIT : Ajouté plus de détails sur la conversation

8voto

opyate Points 3607

8voto

Yuval Adam Points 59423

Les exceptions vérifiées sont un sujet controversé en général, et en Java en particulier (plus tard, j'essaierai de trouver quelques exemples pour les partisans et les opposants à ces exceptions).

En règle générale, le traitement des exceptions devrait s'inspirer de ces lignes directrices, sans ordre particulier :

  • Pour des raisons de maintenabilité, enregistrez toujours les exceptions de sorte que, lorsque vous commencerez à constater des bogues, le journal vous aidera à trouver l'endroit où le bogue a probablement commencé. Ne laissez jamais printStackTrace() ou autre, il y a de fortes chances que l'un de vos utilisateurs reçoive un jour l'une de ces traces de pile, et ait exactement zéro connaissance sur ce qu'il faut en faire.
  • Attrapez les exceptions que vous pouvez gérer, et seulement celles-là, et les manipuler ne les jette pas simplement dans la pile.
  • Il faut toujours attraper une classe d'exception spécifique, et en général, il ne faut jamais attraper le type Exception vous risquez fort d'avaler des exceptions autrement importantes.
  • Ne jamais (jamais) attraper Error s !! ce qui signifie : Ne jamais attraper Throwable s comme Error sont des sous-classes de ces derniers. Error sont des problèmes que vous ne serez probablement jamais en mesure de résoudre (par ex. OutOfMemory ou d'autres problèmes de JVM)

En ce qui concerne 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 à retour booléen peut renvoyer false, mais assurez-vous que les endroits où vous appelez cette méthode sont capables de le gérer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X