Granularité des exceptions

9

J'ai rencontré un débat entre quelques amis et moi. Ils préfèrent les exceptions générales telles que ClientErrorExceptionet ServerErrorExceptionavec détail comme champs de l'exception, alors que je préfère rendre les choses plus spécifiques. Par exemple, je pourrais avoir une poignée d'exceptions comme:

  • BadRequestException
  • AuthenticationFailureException
  • ProductNotFoundException

Chacun de ces éléments est basé sur le code d'erreur renvoyé par l'API.

Suivant les avantages des exceptions, cela semble idiomatique pour Java. Cependant, l'opinion de mes amis n'est pas tout à fait rare.

Existe-t-il un moyen préféré en termes de lisibilité du code et de convivialité de l'API, ou est-ce vraiment une question de préférence?


la source
La page que vous avez liée est probablement la meilleure réponse définitive que nous pouvons obtenir. Vous demandez vraiment un avis. Je peux répondre avec mon expérience et mon opinion, mais ce n'est pas une réponse objective.
marstato
@marstato c'est juste. Je suppose que je cherche en quelque sorte une justification dans ma position. Je préfère m'en tenir à ce que les gens attendent dans les bibliothèques que j'écris, plutôt que de suivre un guide si cela signifie que cela rend mes affaires plus faciles à utiliser, vous savez?
Je suis absolument d'accord. J'ai aussi mes classes d'exception granulaires. En outre, vous pouvez définir abstractet généraliser des classes d'exceptions avec les méthodes getter, puis faire en sorte que les classes granulaires étendent les classes générales. Par exemple AuthenticationFaliureException extends ClientErrorException. De cette façon, chaque utilisateur peut choisir comment il souhaite gérer les exceptions. Mais ça marche plus, évidemment. Cependant, lors de l'écriture d'une application (au lieu d'une bibliothèque), c'est une situation différente à mon humble avis. Dans ce cas, je ne rendrais pas les exceptions plus granulaires que vous n'en avez besoin, par souci de simplicité.
marstato
@marstato c'est en fait ainsi que je l'implémente maintenant. Je suis content que vous soyez d'accord. Je vais laisser la question ouverte pendant la nuit, mais veuillez consolider cela dans un message afin que je puisse au moins vous vérifier en vert

Réponses:

15

La principale différence entre avoir de nombreuses classes d'exceptions différentes et n'en avoir que quelques-unes, avec des informations plus détaillées dans le texte d'erreur (par exemple), est que de nombreuses classes d'exceptions différentes permettent au code appelant de réagir différemment à différents types d'erreurs, tout en ayant Seules quelques classes facilitent la gestion uniforme de toutes sortes d'exceptions.

Il s'agit généralement d'un compromis. Il peut être atténué dans une certaine mesure en utilisant l'héritage (une classe d'exception de base générale pour les appelants qui veulent tout capturer et consigner tout de manière générique, et des exceptions dérivées de cette classe de base pour les appelants qui ont besoin de réactions différentes), mais même cela peut produire un beaucoup de complexité inutile si vous ne faites pas attention et respectez le principe YAGNI. La question directrice devrait donc être ici:

  • Vous attendez-vous vraiment à ce que l'appelant de votre code réagisse différemment, avec un flux de contrôle différent, à ces différents types d'erreurs?

Il n'y a pas de solution unique à cela, pas de «meilleure pratique» braindead que vous pouvez appliquer partout. La réponse à cette question dépend fortement du type de logiciel ou de composant que vous concevez:

  • une application, où vous ou l'équipe avez toute la base de code sous votre contrôle?

  • ou un composant réutilisable pour des tiers, où vous ne connaissez pas tous les appelants potentiels?

  • une application serveur de longue durée, où différents types d'erreurs ne devraient pas immédiatement briser l'ensemble du système et peuvent nécessiter différents types d'atténuation des erreurs?

  • un processus d'application de courte durée où il suffit en cas d'erreur d'afficher un message d'erreur à l'utilisateur puis de redémarrer le processus?

Donc, plus vous en savez sur les appelants potentiels de votre composant, mieux vous pouvez décider du niveau de détail correct pour vos exemptions.

Doc Brown
la source
3
"Vous attendez-vous vraiment à ce que l'appelant de votre code réagisse différemment, avec un flux de contrôle différent, à ces différents types d'erreurs?" c'est une excellente question à poser. Merci, je vais m'en souvenir.
C'est une bonne réponse, je pense, je ne ferais que souligner davantage la nécessité de laisser l'application cliente conduire la conception de levée d'exceptions. Même les généralisations que vous pourriez juger judicieuses d'intégrer à la hiérarchie d'exceptions peuvent ne pas correspondre aux façons dont votre application client (si ce n'est pas vous-même qui l'écrivez) pourrait choisir de généraliser un gestionnaire. Si vous avez plus d'une application cliente ayant des besoins différents, alors ce sera à vous de les équilibrer.
magicduncan
1

La réponse dépend du niveau de rapport d'erreur dont nous parlons.

En général, je suis d'accord avec vos amis, vous ne devriez pas les rendre plus granuleux que nécessaire pour transmettre la cause du problème.

S'il existe une exception courante et bien connue pour référencer null (NullReferenceException), vous ne devez pas créer votre propre MyObjectIsNullException. Cela ne ferait qu'ajouter une couche de confusion pour l'interprète humain, une chose supplémentaire à apprendre qui ne clarifie rien.

Ce n'est que lorsque votre exception est si spéciale qu'il n'y en a pas de prédéfinie qui couvre la cause racine que vous devez créer la vôtre.

Cependant, vous ne devez pas vous arrêter là. L'erreur courante peut se produire dans l'un de vos composants et vous souhaiterez peut-être signaler qu'il y avait un problème dans votre composant . Donc, pas seulement ce qui s'est mal passé, mais aussi où. Il serait alors approprié d'envelopper la première exception dans une MyComponentException. Cela vous donnera le meilleur des deux mondes.

Tout d'abord, il est clair que votre composant a rencontré des problèmes. Sur un levier inférieur, la cause spécifique serait dans l'exception intérieure.

Martin Maat
la source
C'est juste. Vous suggérez donc de ne pas inventer une nouvelle exception lorsqu'une bonne existe déjà?
@rec Oui, car tout le monde connaît déjà les prédéfinis. C'est pour ça qu'ils sont là.
Martin Maat