499 votes

Les arguments contre les exceptions vérifiées

Depuis plusieurs années, je ne parviens pas à obtenir une réponse décente à la question suivante : pourquoi certains développeurs sont-ils si opposés aux exceptions vérifiées ? J'ai eu de nombreuses conversations, j'ai lu des choses sur des blogs, j'ai lu ce que Bruce Eckel avait à dire (la première personne que j'ai vue s'exprimer contre elles).

Je suis en train d'écrire un nouveau code et je fais très attention à la façon dont je traite les exceptions. J'essaie de comprendre le point de vue de la foule "nous n'aimons pas les exceptions vérifiées" et je n'y arrive toujours pas.

Toutes les conversations que j'ai se terminent par la même question qui reste sans réponse... Laissez-moi vous expliquer :

En général (d'après la façon dont Java a été conçu),

  • Error est pour les choses qui ne devraient jamais être prises (VM est allergique aux cacahuètes et quelqu'un a fait tomber un pot de cacahuètes sur lui)
  • RuntimeException est pour les choses que le programmeur a mal faites (le programmeur a oublié la fin d'un tableau).
  • Exception (sauf RuntimeException ) est pour les choses qui sont hors du contrôle du programmeur (le disque se remplit lors de l'écriture dans le système de fichiers, la limite du gestionnaire de fichiers pour le processus a été atteinte et vous ne pouvez plus ouvrir de fichiers).
  • Throwable est simplement le parent de tous les types d'exception.

Un argument courant que j'entends est que si une exception se produit, tout ce que le développeur va faire est de quitter le programme.

Un autre argument courant que j'entends est que les exceptions vérifiées rendent plus difficile le remaniement du code.

Pour l'argument "tout ce que je vais faire est de quitter", je dis que même si vous quittez, vous devez afficher un message d'erreur raisonnable. Si vous vous contentez de gérer les erreurs, vos utilisateurs ne seront pas très heureux lorsque le programme quittera sans indication claire de la raison.

Pour ceux qui pensent que "cela rend difficile le remaniement", cela indique que le niveau d'abstraction approprié n'a pas été choisi. Plutôt que de déclarer qu'une méthode lance un IOException le IOException devrait être transformée en une exception plus adaptée à ce qui se passe.

Je n'ai pas de problème à envelopper Main avec catch(Exception) (ou dans certains cas catch(Throwable) afin de s'assurer que le programme peut se terminer de manière élégante - mais j'attrape toujours les exceptions spécifiques dont j'ai besoin. Cela me permet, au minimum, d'afficher un message d'erreur approprié.

La question à laquelle les gens ne répondent jamais est la suivante :

Si vous lancez RuntimeException au lieu de Exception des sous-classes, alors comment savoir ce que vous êtes censé attraper ?

Si la réponse est catch Exception alors vous traitez les erreurs du programmeur de la même manière que les exceptions du système. Cela me semble erroné.

Si vous attrapez Throwable alors vous traitez les exceptions système et les erreurs VM (et autres) de la même manière. Cela me semble erroné.

Si la réponse est que vous n'attrapez que les exceptions que vous savez être levées, comment savez-vous lesquelles ? Que se passe-t-il lorsque le programmeur X lance une nouvelle exception et oublie de l'attraper ? Cela me semble très dangereux.

Je dirais qu'un programme qui affiche une trace de la pile est mauvais. Les personnes qui n'aiment pas les exceptions vérifiées ne sont-elles pas de cet avis ?

Donc, si vous n'aimez pas les exceptions contrôlées, pouvez-vous expliquer pourquoi et répondre à la question qui n'a pas reçu de réponse ?

Je ne cherche pas à savoir quand utiliser l'un ou l'autre modèle, ce que je cherche c'est pourquoi les personnes s'étendent de RuntimeException parce qu'ils n'aiment pas s'étendre de Exception et/ou pourquoi ils attrapent une exception et relancent ensuite une RuntimeException plutôt que d'ajouter des lancers à leur méthode. Je veux comprendre les raisons pour lesquelles on n'aime pas les exceptions vérifiées.

47 votes

Je ne pense pas que ce soit complètement subjectif - c'est une fonctionnalité de la langue qui a été conçue pour avoir une utilisation spécifique, plutôt que pour que chacun puisse décider de ce à quoi elle sert pour lui-même. Et ce n'est pas spécialement argumentatif, ça répond à l'avance à des réfutations spécifiques que les gens auraient pu facilement trouver.

7 votes

Allez. Considéré comme une caractéristique de la langue, ce sujet a été et peut être abordé de manière objective.

6 votes

@cletus "répondre à votre propre question" si j'avais la réponse je n'aurais pas posé la question !

26voto

Luke Quinane Points 8257

SNR

Tout d'abord, les exceptions contrôlées diminuent le "rapport signal/bruit" du code. Anders Hejlsberg parle également de la programmation impérative et déclarative, un concept similaire. Quoi qu'il en soit, considérez les extraits de code suivants :

Mise à jour de l'interface utilisateur à partir d'un thread non lié à l'interface utilisateur en Java :

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

Mise à jour de l'interface utilisateur à partir d'un thread non lié à l'interface utilisateur en C# :

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

Ce qui me semble beaucoup plus clair. Lorsque vous commencez à faire de plus en plus de travail d'interface utilisateur en Swing, les exceptions vérifiées commencent à devenir vraiment ennuyeuses et inutiles.

Jail Break

Pour mettre en œuvre même les implémentations les plus basiques, comme l'interface List de Java, les exceptions contrôlées en tant qu'outil de conception par contrat tombent à l'eau. Considérez une liste qui est soutenue par une base de données ou un système de fichiers ou toute autre implémentation qui lance une exception vérifiée. La seule mise en œuvre possible consiste à attraper l'exception vérifiée et à la relancer comme une exception non vérifiée :

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

Et maintenant vous devez vous demander quel est l'intérêt de tout ce code ? Les exceptions vérifiées ne font qu'ajouter du bruit, l'exception a été attrapée mais pas traitée et la conception par contrat (en termes d'exceptions vérifiées) a échoué.

Conclusion

  • La capture des exceptions est différente de leur traitement.
  • Les exceptions vérifiées ajoutent du bruit au code.
  • Le traitement des exceptions fonctionne bien en C# sans eux.

J'ai fait un blog sur ce sujet précédemment .

23voto

Le Dude Points 294

Artima a publié une interview avec l'un des architectes de .NET, Anders Hejlsberg, qui aborde avec acuité les arguments contre les exceptions vérifiées. Un petit avant-goût :

La clause throws, du moins telle qu'elle est implémentée en Java, ne vous oblige pas nécessairement à traiter les exceptions, mais si vous ne les traitez pas, elle vous oblige à reconnaître précisément les exceptions susceptibles de passer. Elle vous oblige à attraper les exceptions déclarées ou à les placer dans votre propre clause throws. Pour contourner cette exigence, les gens font des choses ridicules. Par exemple, ils décorent chaque méthode avec "throws Exception". Cela va complètement à l'encontre de la fonctionnalité, et vous venez de faire écrire au programmeur encore plus de bêtises. Cela n'aide personne.

21voto

Travis Points 6062

J'ai d'abord été d'accord avec vous, car j'ai toujours été en faveur des exceptions vérifiées, et j'ai commencé à réfléchir aux raisons pour lesquelles je n'aime pas ne pas avoir d'exceptions vérifiées dans .Net. Mais je me suis ensuite rendu compte que je n'aime pas vraiment les exceptions vérifiées.

Pour répondre à votre question, oui, j'aime que mes programmes montrent des traces de pile, de préférence des traces très laides. Je veux que l'application explose en un horrible tas de messages d'erreur les plus laids que l'on puisse souhaiter voir.

Et la raison en est que, s'il fait ça, je dois le réparer, et je dois le réparer tout de suite. Je veux savoir immédiatement qu'il y a un problème.

Combien de fois traitez-vous réellement les exceptions ? Je ne parle pas d'attraper les exceptions, je parle de les traiter ? C'est trop facile d'écrire ce qui suit :

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

Et je sais que vous pouvez dire que c'est une mauvaise pratique, et que "la réponse" est de faire quelque chose avec l'exception (laissez-moi deviner, l'enregistrer ?), mais dans le monde réel (tm), la plupart des programmeurs ne le font tout simplement pas.

Donc oui, je ne veux pas attraper les exceptions si je n'ai pas à le faire, et je veux que mon programme explose de façon spectaculaire quand je me plante. Echouer silencieusement est le pire résultat possible.

21voto

Esko Luontola Points 53877

L'article Exceptions Java efficaces explique joliment quand utiliser des exceptions non vérifiées et quand utiliser des exceptions vérifiées. Voici quelques citations de cet article pour souligner les points principaux :

Contingence : Une condition attendue exigeant une réponse alternative de la part d'une méthode qui peut être exprimée en termes de l'objectif prévu de la méthode. L'appelant de la méthode s'attend à ce genre de conditions et a une stratégie pour y faire face.

Défaut : Une condition non planifiée qui empêche une méthode d'atteindre son objectif prévu et qui ne peut être décrite sans faire référence à la mise en œuvre interne de la méthode.

(SO n'autorise pas les tableaux, vous pouvez donc lire ce qui suit à partir de la page d'accueil du site Web de SO. page originale ...)

Contingence

  • est considéré comme : Une partie de la conception
  • On s'attend à ce que cela se produise : Régulièrement mais rarement
  • Qui s'en soucie : Le code amont qui invoque la méthode
  • Exemples : Modes de retour alternatifs
  • Meilleure cartographie : Une exception vérifiée

Défauts

  • Est considéré comme tel : Une mauvaise surprise
  • On s'attend à ce que cela se produise : Jamais
  • Qui s'en soucie : Les personnes qui doivent résoudre le problème
  • Exemples : Bogues de programmation, dysfonctionnements du matériel, erreurs de configuration, fichiers manquants, serveurs indisponibles
  • Meilleure cartographie : Une exception non vérifiée

20voto

Mario Ortegón Points 8563

Au cours des trois dernières années, j'ai travaillé avec plusieurs développeurs sur des applications relativement complexes. Nous avons une base de code qui utilise assez souvent des exceptions vérifiées avec une gestion d'erreur appropriée, et une autre qui ne le fait pas.

Jusqu'à présent, j'ai trouvé plus facile de travailler avec la base de code avec des exceptions vérifiées. Lorsque j'utilise l'API de quelqu'un d'autre, il est agréable de pouvoir voir exactement à quel type de conditions d'erreur je peux m'attendre lorsque j'appelle le code et de les gérer correctement, soit en les consignant, en les affichant ou en les ignorant (oui, il existe des cas valables pour ignorer les exceptions, comme la mise en œuvre d'un ClassLoader). Cela donne au code que j'écris la possibilité de se rétablir. Toutes les exceptions d'exécution que je propage jusqu'à ce qu'elles soient mises en cache et traitées avec un code générique de gestion des erreurs. Lorsque je trouve une exception vérifiée que je ne veux pas vraiment traiter à un niveau spécifique, ou que je considère comme une erreur de logique de programmation, alors je l'enveloppe dans une RuntimeException et je la laisse remonter. Ne jamais, jamais avaler une exception sans une bonne raison (et les bonnes raisons de le faire sont plutôt rares).

Lorsque je travaille avec un code qui ne comporte pas d'exceptions vérifiées, il m'est un peu plus difficile de savoir à l'avance ce à quoi je peux m'attendre en appelant la fonction, ce qui peut entraîner de graves problèmes.

Tout ceci est bien sûr une question de préférence et de compétence du développeur. Les deux façons de programmer et de gérer les erreurs peuvent être aussi efficaces (ou inefficaces) l'une que l'autre, et je ne dirais donc pas qu'il existe une méthode unique.

Dans l'ensemble, je trouve qu'il est plus facile de travailler avec des exceptions vérifiées, surtout dans les grands projets avec beaucoup de développeurs.

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