web-dev-qa-db-fra.com

Arguments contre la suppression des erreurs

J'ai trouvé un morceau de code comme celui-ci dans l'un de nos projets:

    SomeClass QueryServer(string args)
    {
        try
        {
            return SomeClass.Parse(_server.Query(args));
        }
        catch (Exception)
        {
            return null;
        }
    }

Pour autant que je sache, la suppression d'erreurs comme celle-ci est une mauvaise pratique, car elle détruit les informations utiles de l'exception du serveur d'origine et fait continuer le code alors qu'il devrait se terminer.

Quand est-il approprié de supprimer complètement toutes les erreurs de ce type?

34
user626528

Imaginez du code avec des milliers de fichiers à l'aide d'un tas de bibliothèques. Imaginez que tous soient codés comme ceci.

Imaginez, par exemple, qu'une mise à jour de votre serveur entraîne la disparition d'un fichier de configuration; et maintenant tout ce que vous avez est une trace de pile est une exception de pointeur nul lorsque vous essayez d'utiliser cette classe: comment résoudriez-vous cela? Cela peut prendre des heures, au moins la simple journalisation de la trace de pile brute du fichier introuvable [chemin du fichier] peut vous permettre de résoudre momentanément.

Ou pire encore: un échec dans l'une des bibliothèques que vous utilisez après une mise à jour qui fait planter votre code plus tard. Comment pouvez-vous retracer cela à la bibliothèque?

Même sans une gestion robuste des erreurs

throw new IllegalStateException("THIS SHOULD NOT HAPPENING")

ou

LOGGER.error("[method name]/[arguments] should not be there")

peut vous faire gagner des heures.

Mais il y a des cas où vous pourriez vraiment vouloir ignorer l'exception et retourner null comme ceci (ou ne rien faire). Surtout si vous intégrez du code hérité mal conçu et que cette exception est attendue comme un cas normal.

En fait, lorsque vous faites cela, vous devez simplement vous demander si vous ignorez vraiment l'exception ou si vous "gérez correctement" vos besoins. Si le retour de null "gère correctement" votre exception dans ce cas donné, faites-le. Et ajoutez un commentaire expliquant pourquoi c'est la bonne chose à faire.

Les meilleures pratiques sont des choses à suivre dans la plupart des cas, peut-être 80%, peut-être 99%, mais vous trouverez toujours un cas Edge où elles ne s'appliquent pas. Dans ce cas, laissez un commentaire pourquoi vous ne suivez pas la pratique pour les autres (ou même vous-même) qui liront votre code des mois plus tard.

52
Walfrat

Il y a des cas où ce modèle est utile - mais ils sont généralement utilisés lorsque l'exception générée n'aurait jamais dû l'être (c'est-à-dire lorsque les exceptions sont utilisées pour un comportement normal).

Par exemple, imaginez que vous avez une classe qui ouvre un fichier et le stocke dans l'objet renvoyé. Si le fichier n'existe pas, vous pouvez considérer que ce n'est pas un cas d'erreur, vous retournez null et laissez l'utilisateur créer un nouveau fichier à la place. La méthode d'ouverture de fichier peut lever une exception ici pour indiquer qu'aucun fichier n'est présent, donc l'attraper silencieusement peut être une situation valide.

Cependant, ce n'est pas souvent que vous voudriez faire cela, et si le code est jonché d'un tel modèle, vous voudriez y faire face maintenant. À tout le moins, je m'attendrais à une telle capture silencieuse pour écrire une ligne de journal disant que c'est ce qui s'est passé (vous avez le traçage, à droite) et un commentaire pour expliquer ce comportement.

14
gbjbaanb

Cela dépend à 100% du contexte. Si l'appelant d'une telle fonction a la nécessité d'afficher ou de consigner des erreurs quelque part, cela n'a évidemment aucun sens. Si l'appelant ignorait toutes les erreurs ou tous les messages d'exception, cela n'aurait pas beaucoup de sens de renvoyer l'exception ou son message. Lorsque l'exigence est de faire que le code se termine uniquement sans afficher de raison, un appel

   if(QueryServer(args)==null)
      TerminateProgram();

serait probablement suffisant. Vous devez vous demander si cela rendra difficile la recherche de la cause de l'erreur par l'utilisateur - si tel est le cas, ce n'est pas la bonne forme de gestion des erreurs. Cependant, si le code appelant ressemble à ceci

  result = QueryServer(args);
  if(result!=null)
      DoSomethingMeaningful(result);
  // else ??? Mask the error and let the user run into trouble later

alors vous devriez avoir un argument avec le développeur lors de la révision du code. Si quelqu'un implémente une telle forme de gestion des non-erreurs simplement parce qu'il est trop paresseux pour se renseigner sur les exigences correctes, alors ce code ne devrait pas entrer en production, et l'auteur du code devrait être informé des conséquences possibles d'une telle paresse .

7
Doc Brown

Dans les années que j'ai passées à programmer et à développer des systèmes, il n'y a que deux situations où j'ai trouvé le modèle en question utile (dans les deux cas, la suppression contenait également la journalisation de l'exception levée, je ne considère pas le catch simple et null retour comme bonne pratique).

Les deux situations sont les suivantes:

1. Lorsque l'exception n'a pas été considérée comme un état exceptionnel

C'est lorsque vous effectuez une opération sur certaines données, qui peuvent être levées, vous savez qu'elles peuvent lever, mais vous souhaitez toujours que votre application continue de fonctionner, car vous n'avez pas besoin des données traitées. Si vous les recevez, c'est bien, sinon, c'est aussi bien.

Certains attributs facultatifs d'une classe peuvent venir à l'esprit.

2. Lorsque vous fournissez une nouvelle implémentation (meilleure, plus rapide?) D'une bibliothèque en utilisant une interface déjà utilisée dans une application

Imaginez que vous ayez une application utilisant une sorte de vieille bibliothèque, qui n'a pas levé d'exceptions mais a renvoyé null par erreur. Vous avez donc créé un adaptateur pour cette bibliothèque, copiant à peu près l'API d'origine de la bibliothèque, et utilisez cette nouvelle interface (toujours non lancée) dans votre application et gérez vous-même les vérifications null.

Une nouvelle version de la bibliothèque arrive, ou peut-être une bibliothèque complètement différente offrant la même fonctionnalité, qui, au lieu de renvoyer nulls, lève des exceptions et vous souhaitez l'utiliser.

Vous ne voulez pas divulguer les exceptions à votre application principale, vous devez donc les supprimer et les enregistrer dans l'adaptateur que vous créez pour encapsuler cette nouvelle dépendance.


Le premier cas n'est pas un problème, c'est le comportement souhaité du code. Dans la deuxième situation, cependant, si partout la valeur de retour null de l'adaptateur de bibliothèque signifie vraiment une erreur, la refactorisation de l'API pour lever une exception et la rattraper au lieu de vérifier null peut être ( et le code est généralement) une bonne idée.

Personnellement, je n'utilise la suppression des exceptions que pour le premier cas. Je ne l'ai utilisé que pour le deuxième cas, lorsque nous n'avions pas le budget pour faire fonctionner le reste de l'application avec les exceptions au lieu de nulls.

5
Andy