Exceptions pour «erreur de programmation» - Mon approche est-elle valable?

9

J'essaie actuellement d'améliorer mon utilisation des exceptions et j'ai trouvé la distinction importante entre les exceptions qui signifient des erreurs de programmation (par exemple, quelqu'un a passé null comme argument ou appelé une méthode sur un objet après sa suppression) et celles qui signifient un échec dans le opération qui n'est pas la faute de l'appelant (par exemple une exception d'E / S).

Comment ces deux types d'exceptions devraient-ils être traités différemment? Pensez-vous que les exceptions aux erreurs doivent être explicitement documentées, ou est-ce suffisant pour documenter les conditions préalables pertinentes? Et pouvez-vous laisser de côté la documentation d'une condition préalable ou d'une exception d'erreur si elle doit être évidente (par exemple, ObjectDisposedExceptionlors de l'appel d'une méthode sur un objet supprimé)

Medo42
la source
Je distingue généralement une autre catégorie d'exceptions: les exceptions commerciales. Ceux-ci se réfèrent aux conditions d'erreur dont l'utilisateur est intéressé par les détails. Je fais généralement vérifier ces exceptions.
beluchin

Réponses:

2

Je pense que vous êtes sur la bonne voie. Ni lancer, attraper ni documenter toutes les exceptions potentiellement jetables n'a beaucoup de sens. Il y a des moments où la rigueur du produit nécessite un degré plus élevé d'emploi exceptionnel et de documentation (par exemple, certains aspects critiques de la sécurité d'un système).

La stratégie consistant à être plus défensif, à l'aide de concepts contractuels pour identifier les conditions préalables (et postconditions) sur les appelants en aval en particulier (par exemple, tout ce qui ressemble à un membre public ou protégé) sera souvent plus efficace et plus flexible. Cela s'applique non seulement à la mise en œuvre, mais à la documentation. Si les développeurs savent ce qui est attendu, ils sont plus susceptibles de suivre les règles et moins susceptibles d'être confus ou d'utiliser à mauvais escient le code que vous avez écrit.

Certaines des choses courantes qui devraient être documentées incluent le cas des paramètres nuls. Souvent, il y a une conséquence pour leur utilisation qui conduit le résultat à quelque chose qui ne serait normalement pas prévu, mais qui est autorisé et utilisé pour diverses raisons, parfois pour plus de flexibilité. En tant que consommateur d'un membre dont les paramètres autorisent des valeurs nulles ou autres valeurs non rationnelles spéciales (comme un temps négatif ou des quantités négatives), je m'attends à les voir identifiés et expliqués.

Pour les paramètres non nuls, en tant que consommateur d'un membre public ou protégé, je veux savoir que null n'est pas autorisé. Je veux savoir quelle est la plage de valeurs valide dans le contexte donné. Je veux connaître les conséquences de l'utilisation de valeurs qui sont en dehors de la plage normale, mais qui sont par ailleurs valides dans un contexte d'appel différent (par exemple, la valeur du type est généralement valide pour toute opération, mais pas ici - comme un paramètre booléen qui ne fonctionne pas ne vous attendez pas à false comme valeur valide.

En ce qui concerne la plate-forme ou les interfaces bien connues, je ne pense pas que vous deviez aller à l'extrême pour le documenter. Cependant, parce que vous avez la possibilité, en tant que développeur, de varier l'implémentation de n'importe quel guide de plateforme, notez comment il s'ensuit que ce guide peut être utile.

Spécifiques à IDisposable, les implémentations de cette interface offrent souvent une méthode alternative préférée au processus d'élimination explicite. Dans ces cas, mettez en surbrillance la méthode préférée et notez que l'élimination explicite n'est pas préférée.

JustinC
la source
Je comprends la nécessité de documenter les valeurs autorisées, mais si vous voyez une fonction performReadCommand(ICommand cmd, int replySizeBytes)- vous attendriez-vous à ce que null soit acceptable pour cmd, ou une valeur négative pour replySizeBytes? La documentation de l'OMI ne serait nécessaire que si ces valeurs étaient réellement autorisées, et vous devriez probablement déterminer si 0 est valide pour replySizeBytes.
Medo42
Ils pourraient tous deux être "valides" selon la mise en œuvre. Vous êtes ou moi, et même tout le monde ici ne s'attendrait pas à ce que des valeurs nulles ou négatives soient appropriées pour cette signature, mais elles pourraient l'être. Considérez un environnement où le lecteur est un processus bloquant et persistant, et lorsque la cmd est nulle, cela pourrait signifier de ne pas modifier la commande utilisée par le lecteur, et passez à la lecture suivante à l'aide de la commande précédente. Dans l'idéal, dans ce cas, une signature de méthode plus appropriée qui engage le cmd en tant que paramètre serait définie et utilisée, mais il se peut que ce ne soit pas le cas.
JustinC
2

Voici mes propres pensées. Notez que je ne suis pas trop certain que c'est la meilleure façon de procéder, c'est pourquoi j'ai créé cette question en premier lieu.

Pour autant que je sache, il est peu logique qu'un appelant immédiat gère réellement les exceptions d'erreur de programmation, il devrait plutôt s'assurer que les conditions préalables sont remplies. Seuls les gestionnaires d'exceptions «externes» aux limites des tâches doivent les intercepter, afin qu'ils puissent continuer à faire fonctionner le système en cas d'échec d'une tâche.

Afin de garantir que le code client peut intercepter proprement les exceptions "échec" sans intercepter les exceptions d'erreur par erreur, je crée maintenant mes propres classes d'exception pour toutes les exceptions d'échec et je les documente dans les méthodes qui les déclenchent. Je leur ferais vérifier les exceptions en Java.

Jusqu'à récemment, j'essayais de documenter toutes les exceptions qu'une méthode pouvait générer, mais cela crée parfois une liste involontaire qui doit être documentée dans chaque méthode de la chaîne d'appels jusqu'à ce que vous puissiez montrer que l'erreur ne se produira pas. Au lieu de cela, je documente maintenant les conditions préalables dans les descriptions de résumé / paramètre et ne mentionne même pas ce qui se passe si elles ne sont pas remplies. L'idée est que les gens ne devraient pas essayer de capturer ces exceptions de toute façon, il n'est donc pas nécessaire de documenter leurs types.

Pour documenter les conditions préalables, énoncer l'évidence ne fait qu'encombrer inutilement - si passer null à une méthode n'a aucun sens évident, l'appelant doit s'attendre à une exception s'il passe null de toute façon, même si cela n'est pas documenté. Il en va de même pour le cas d'ObjectDisposedException - cette interface est si largement utilisée que quelqu'un appelant Dispose serait conscient de la responsabilité de s'assurer que personne ne continuera à utiliser l'objet.

Medo42
la source
1

Une règle d'or raisonnable:

  1. Saisissez toute exception que vous pouvez corriger.
  2. Attrapez, commentez et repoussez toute exception que vous ne pouvez pas corriger mais pouvez dire quelque chose d'utile.
  3. Ne détectez aucune exception que vous ne pouvez pas traiter ou diagnostiquer mieux que le traitement par défaut.

Vous voudrez peut-être avoir un gestionnaire d'exceptions non fatal de niveau supérieur pour intercepter tout ce qui ne peut pas être géré ci-dessous pour empêcher votre application de tomber, mais cela dépendra fortement de votre application particulière. Par exemple, les applications iOS préfèrent intercepter autant que possible; une application en ligne de commande peut être parfaitement OK si elle n'intercepte pratiquement aucune exception.

Joe McMahon
la source
0

Même Java ne "documente" pas toutes les exceptions. Malgré l'exigence que chaque thrown exception soit mentionnée dans une throwsclause, n'importe quelle ligne de code peut lancer un RuntimeExceptionsans avoir besoin de le déclarer dans la signature de la méthode.

Ross Patterson
la source
Cela va à l'encontre des conseils populaires - spécifiquement, Effective Java, 2nd ed, Item 62 est "Documenter toutes les exceptions levées par chaque méthode". En fait, Bloch reconnaît que les exceptions non vérifiées sont généralement des erreurs de programmation, mais qu'elles doivent également être documentées attentivement, car c'est "la meilleure façon" de documenter les conditions préalables d'une méthode. Je ne sais pas si je suis d'accord cependant. Si je documente les exceptions, je les fais partie du contrat d'interface et je devrai peut-être ajouter du code pour qu'elles restent les mêmes si mon implémentation change.
Medo42
1
Pour chaque expert, il y a un contre-expert. Bruce Eckel, auteur du plutôt célèbre Thinking in Java , était une fois dans le camp des exceptions pro-vérifiées et a changé de camp. Il y a une belle discussion entre Eckels et Anders Hejlsberg , le créateur de C #, qui n'a pas vérifié les exceptions.
Ross Patterson
0

La façon standard de le faire est avec l'approche java. Les erreurs de programmation doivent être des exceptions non vérifiées et ne doivent pas être interceptées pour garantir une panne rapide. Les erreurs de contrat sont des exceptions vérifiées et doivent être traitées de manière appropriée par le client.

J.Ashworth
la source
2
Considérez-vous (par exemple) une valeur NULL calculée (ou une valeur d'erreur) comme une erreur de programmation ou une erreur de contrat ? Je vais avec votre distinction, mais la frontière n'est pas aussi claire :)
Andrew
Si la valeur null est calculée à partir d'un argument passé (tel qu'un argument null ou quelque chose de non valide), c'est une erreur de contrat. Si le null est calculé à cause d'un morceau de mauvais code, c'est une erreur de programmation. Si le null est censé être calculé, ce n'est pas une erreur. Je ne vois vraiment aucune ambiguïté dans cet exemple.
J.Ashworth