MethodA appelle un MethodB qui à son tour appelle MethodC.
Il n'y a AUCUNE gestion des exceptions dans MethodB ou MethodC. Mais il existe une gestion des exceptions dans MethodA.
Dans MethodC, une exception se produit.
Maintenant, cette exception se propage à MethodA, qui la gère de manière appropriée.
Quel est le probleme avec ca?
Dans mon esprit, à un moment donné, un appelant exécutera MethodB ou MethodC, et lorsque des exceptions se produisent dans ces méthodes, ce qui sera gagné en gérant les exceptions à l'intérieur de ces méthodes, qui est essentiellement juste un bloc try/catch/finally au lieu de simplement laisser les faire bouillonner jusqu'à l'appelé?
La déclaration ou le consensus autour de la gestion des exceptions est de lancer lorsque l'exécution ne peut pas continuer à cause de cela - une exception. Je comprends ça. Mais pourquoi ne pas attraper l'exception plus haut dans la chaîne au lieu d'avoir des blocs try/catch tout en bas.
Je le comprends lorsque vous avez besoin de libérer des ressources. C'est une toute autre affaire.
En règle générale, ne prenez pas d'exceptions sauf si vous savez quoi en faire. Si MethodC lève une exception, mais MethodB n'a aucun moyen utile de la gérer, il doit permettre à l'exception de se propager jusqu'à MethodA.
Les seules raisons pour lesquelles une méthode devrait avoir un mécanisme de capture et de retour sont les suivantes:
Sinon, la capture d'exceptions au mauvais niveau a tendance à entraîner un code qui échoue silencieusement sans fournir de rétroaction utile au code appelant (et, finalement, à l'utilisateur du logiciel). L'alternative d'attraper une exception et de la renvoyer immédiatement est inutile.
Quel est le probleme avec ca ?
Absolument rien.
Maintenant, cette exception se propage à MethodA, qui la gère de manière appropriée.
"gère correctement" est la partie importante. C'est le nœud de la gestion structurée des exceptions.
Si votre code peut faire quelque chose d '"utile" avec une exception, allez-y. Sinon, laissez-le bien tranquille.
. . . pourquoi ne pas attraper l'exception plus haut dans la chaîne au lieu d'avoir des blocs try/catch tout en bas.
C'est exactement ce que vous devriez faire. Si vous lisez du code qui a un gestionnaire/rethrowers "tout en bas", alors vous lisez [probablement] du code assez médiocre.
Malheureusement, certains développeurs voient simplement les blocs catch comme du code "passe-partout" qu'ils jettent (sans jeu de mots) à chaque méthode qu'ils écrivent, souvent parce qu'ils ne "reçoivent" pas vraiment la gestion des exceptions et pensent qu'ils doivent ajouter quelque chose pour que les exceptions n'échappent pas et ne tuent pas leur programme.
Une partie de la difficulté ici est que, la plupart du temps, ce problème ne sera même pas remarqué, car les exceptions ne sont pas levées tout le temps mais, quand elles sont =, le programme va perdre énormément de temps et d'efforts en décochant progressivement la pile d'appels pour arriver à un endroit qui fait réellement quelque chose d'utile avec l'exception.
Vous devez faire la différence entre les bibliothèques et les applications.
Lorsque vous concevez une bibliothèque, à un moment donné, vous devez penser à ce qui peut mal tourner. Les paramètres peuvent être dans la mauvaise plage ou null
, les ressources externes peuvent être indisponibles, etc.
Votre bibliothèque n'aura le plus souvent aucun moyen de les traiter de manière sensée . La seule solution raisonnable consiste à lever une exception appropriée et à laisser le développeur de l'application s'en occuper.
Lorsqu'une exception est interceptée, j'aime les classer comme Erreurs ou Erreurs fatales. Un Erreur normal signifie qu'une seule opération dans mon application a échoué. Par exemple, un document ouvert n'a pas pu être enregistré, car la destination n'était pas accessible en écriture. La seule chose sensée à faire pour l'Application est d'informer l'utilisateur que l'opération n'a pas pu être effectuée avec succès, de donner des informations lisibles par l'homme en ce qui concerne le problème, puis de laisser l'utilisateur décider quoi faire ensuite.
A Erreur fatale est une erreur dont la logique principale de l'application ne peut pas récupérer. Par exemple, si le pilote du périphérique graphique tombe en panne dans un jeu vidéo, l'application n'a aucun moyen d'informer "gracieusement" l'utilisateur. Dans ce cas, un fichier journal doit être écrit et, si possible, l'utilisateur doit être informé d'une manière ou d'une autre.
Même dans un cas aussi grave, l'application doit gérer cette exception de manière significative. Cela peut inclure l'écriture d'un fichier journal, l'envoi d'un rapport de plantage, etc. Il n'y a aucune raison pour que l'application ne réponde pas à l'exception d'une manière ou d'une autre.
Ce qui ne va pas avec le modèle que vous décrivez, c'est que la méthode A n'aura aucun moyen de distinguer entre trois scénarios:
La méthode B a échoué de manière anticipée.
La méthode C a échoué d'une manière non prévue par la méthode B, mais pendant que la méthode B effectuait une opération qui pouvait être abandonnée en toute sécurité.
La méthode C a échoué d'une manière non prévue par la méthode B, mais pendant que la méthode B effectuait une opération qui mettait les choses dans un état incohérent supposé temporaire que B n'a pas nettoyé en raison de l'échec de C.
La seule façon dont la méthode A pourra distinguer ces scénarios sera si l'exception levée à partir de B inclut des informations suffisantes à cet effet, ou si le déroulement de la pile pour la méthode B fait que l'objet est laissé dans un explicitement invalidé état. Malheureusement, la plupart des frameworks d'exception rendent les deux modèles maladroits, forçant les programmeurs à prendre des décisions de conception "moins néfastes".