levée d'exception d'exécution dans l'application Java

12

Je travaille en tant qu'entrepreneur qui conçoit une application Java d'entreprise pour mon client dans le rôle de responsable technique. L'application sera utilisée par les utilisateurs finaux et il y aura une équipe de support qui prendra en charge l'application lorsque nous quitterons.

Les autres pistes techniques avec lesquelles je travaille ont l'impression que la gestion des exceptions rendra le code sale. Le système doit lever les exceptions vérifiées uniquement à partir du niveau Service et le reste du code doit lever les exceptions d'exécution de tous les autres niveaux de sorte qu'il n'est pas nécessaire de gérer les exceptions non vérifiées.

Quel est le besoin de lever une exception non contrôlée dans une application métier?

D'après mon expérience dans le passé sur les exceptions d'exécution:

1) Les exceptions non vérifiées rendent le code imprévisible car elles n'apparaissent pas même dans Javadoc.

2) Lancer des exceptions non vérifiées dans une application métier est insensé, car lorsque vous la lancez et que cela va directement sur le visage des utilisateurs, comment l'expliquez-vous à l'utilisateur? J'ai vu assez d'applications Web qui montrent 500 -Internal Error. Contact Administratorque cela ne signifie rien pour un utilisateur final ou pour l'équipe de support qui gère l'application.

3) Le lancement d'exceptions d'exécution oblige les utilisateurs de la classe qui lancent l'exception à parcourir le code source pour déboguer et voir pourquoi l'exception est levée. Cela ne peut être évité que si le Javadoc de l'exception d'exécution se trouve être parfaitement documenté, ce qui, je trouve, n'est jamais un cas.

randominstanceOfLivingThing
la source
Pouvez-vous expliquer ce qu'est le niveau de service? La couche est-elle la plus éloignée de l'utilisateur ou la plus proche de l'utilisateur?
Manoj R
Pourquoi cette question ne rentre-t-elle pas dans SO?
Bjarke Freund-Hansen
@Manoj R: Ni l'un ni l'autre. Un niveau ou une couche de service est l'endroit où les règles et la logique métier sont exécutées, séparées des détails de la technologie DB et de la présentation client.
Michael Borgwardt
6
Les exceptions non vérifiées n'apparaissent pas [...] même dans le Javadoc. => ils apparaîtront si vous les documentez avec la @throwsbalise, ce qui est d'ailleurs une bonne pratique.
assylias
4
@SureshKoya Effective Java, Item 62: Documentez toutes les exceptions levées par chaque méthode. Extraire: utilisez la @throwsbalise Javadoc pour documenter chaque exception non vérifiée qu'une méthode peut lancer, mais n'utilisez pas le mot clé throws pour inclure des exceptions non vérifiées dans la déclaration de méthode.
assylias

Réponses:

13

Les exceptions vérifiées sont un expriment échoué dans la conception du langage. Ils vous obligent à avoir des abstractions extrêmement fuyantes et un code sale. Ils doivent être évités autant que possible.

Quant à vos points:

1) Les exceptions vérifiées rendent le code sale et non moins imprévisible car elles apparaissent partout .

2) Comment une exception vérifiée est-elle mieux affichée à l'utilisateur? La seule vraie différence entre les exceptions vérifiées et non contrôlées est technique et n'affecte que le code source.

3) Avez-vous déjà entendu parler de traces de pile? Ils vous indiquent exactement où l'exception a été levée, qu'elle soit cochée ou décochée. En fait, les exceptions vérifiées ont tendance à être pires pour le débogage, car elles sont souvent encapsulées, ce qui conduit à des traces de pile plus longues et plus laides, ou même perdues ensemble parce que l'encapsulation a été mal effectuée.

Il existe deux types d'exceptions: celles qui se produisent "normalement" et sont généralement gérées très près de l'endroit où elles se produisent, et celles qui sont vraiment exceptionnelles et peuvent être gérées de manière générique dans une couche très élevée (il suffit d'annuler l'action en cours et de la consigner / afficher une erreur).

Les exceptions vérifiées étaient une tentative de mettre cette distinction dans la syntaxe du langage au moment où les exceptions sont définies. Les problèmes avec cela sont

  • La distinction appartient vraiment à l' appelant , pas au code qui lève l'exception
  • C'est complètement orthogonal à la signification sémantique d'une exception, mais le lier à la hiérarchie des classes vous oblige à mélanger les deux
  • Le seul point des exceptions est que vous pouvez décider à quel niveau les attraper sans risquer de perdre une erreur silencieusement ou d'avoir à polluer le code à des niveaux intermédiaires ou; les exceptions vérifiées perdent ce deuxième avantage.
Michael Borgwardt
la source
1. Eh bien, en cas d'exception non vérifiée, vous ne saurez même pas si un appel pourrait entraîner une non vérifiée. 2. Quand vous avez dit "C'est complètement orthogonal au sens sémantique d'une exception, mais le lier à la hiérarchie des classes vous oblige à mélanger les deux", je suppose que vous vouliez dire hiérarchie d'appel au lieu de hiérarchie de classe.
randominstanceOfLivingThing
2
@Suresh Koya: Non, je voulais dire la hiérarchie des classes, le fait qu'une déclaration SQLException extends Exceptionsignifie que choisir de lancer un SQLExceptioncar une erreur a à voir avec l'accès à la base de données signifie que l'exception est vérifiée. Et vous pouvez très bien documenter les exceptions non contrôlées dans les commentaires Javadoc. Fonctionne bien pour toutes les autres langues.
Michael Borgwardt
1
@MichaelBorgwardt "Les exceptions vérifiées sont un expriment échoué dans la conception de la langue", est-ce votre opinion personnelle sinon je voudrais en savoir plus. Tout lien authentique serait d'une grande aide.
Vipin
2
@Vipin: C'est mon opinion personnelle, mais celle que de nombreuses autres personnes partagent, par exemple Bruce Eckel: mindview.net/Etc/Discussions/CheckedExceptions - et le fait qu'aucun autre langage n'a adopté d'exceptions vérifiées dans les 20 ans depuis l'introduction de Java parle pour lui-même ...
Michael Borgwardt
2

À mon avis, le type d'exception levée dépend de ce que fait votre code.

Je jette des exceptions vérifiées quand je m'attends à ce qu'elles se produisent assez souvent (les utilisateurs saisissent des données douteuses par exemple) et je m'attends à ce que le code appelant gère la situation.

Je lance des exceptions non vérifiées / d'exécution (pas aussi souvent que vérifiées) lorsque j'ai une situation rare que je ne m'attends pas à ce que le code appelant gère. Un exemple pourrait être une sorte d'erreur bizarre liée à la mémoire que je ne m'attends jamais à se produire. Les types d'erreurs que vous prévoyez de supprimer l'application sont décochés.

Aucune exception ne doit apparaître devant un utilisateur sans un certain niveau de support. Même si c'est juste un "s'il vous plaît couper et coller ce vidage d'erreur dans un e-mail". Rien n'est plus ennuyeux pour un utilisateur que de se faire dire qu'il y a une erreur, mais étant donné qu'aucun détail ni aucune action ne peut être entrepris pour le corriger, il est corrigé.

Je suppose que la philosophie que vous mentionnez provient de l'une des deux sources suivantes:

  • Des programmeurs paresseux essayant d'éviter de faire du travail.
  • Ou des développeurs qui ont dû prendre en charge du code qui est allé dans l'autre sens. c'est-à-dire la gestion des erreurs excessives. Le type de code qui contient de grandes quantités de gestion des exceptions, dont une grande partie ne fait rien, ou pire encore, est utilisé pour le contrôle de flux et à d'autres fins incorrectes.
drekka
la source
Si quelque chose peut arriver assez souvent, ce n'est pas une exception. Mais convenu que l'erreur ne devrait pas être levée, ce dont l'utilisateur n'a aucune idée.
Manoj R
Je suis d'accord sur ta philosophie. Sur une application que vous développez pour les utilisateurs, quel sera le besoin de lever une exception Runtime?
randominstanceOfLivingThing
De plus, même si une exception Runtime est levée, je préfère la convention indiquée par @assylias.
randominstanceOfLivingThing
1

Si vous ne voulez pas d'exceptions d'exécution non contrôlées, alors pourquoi utilisez-vous java? Il existe la possibilité d'exceptions d'exécution presque partout - notamment les exceptions NullPointerException, ArithmeticException, ArrayIndexOutOfBounds, etc.

Et lorsque vous lisez une fois les fichiers journaux d'un système J2EE, comme, par exemple, une installation SAP NetWeaver, vous verrez que de telles exceptions se produisent littéralement tout le temps.

Ingo
la source
De la spécification du langage Java: "Les classes d'exception d'exécution (RuntimeException et ses sous-classes) sont exemptées de la vérification au moment de la compilation car, de l'avis des concepteurs du langage de programmation Java, l'obligation de déclarer de telles exceptions n'aiderait pas de manière significative à établir l'exactitude La plupart des opérations et des constructions du langage de programmation Java peuvent entraîner des exceptions d'exécution. "
randominstanceOfLivingThing
1
Les exceptions d'exécution dont vous parlez sont celles qui proviennent de Java, car le système d'exécution Java n'a aucune idée de la raison pour laquelle vous essayez d'effectuer l'appel spécifique. Par exemple: une exception NullPointerException se produirait si vous appelez une méthode qui est nulle. Dans ce cas, le système d'exécution Java n'a pas de mot juste pour la folie des programmeurs et giflerait l'appelant avec une NullPointerException. Le plus triste, c'est que l'appelant vous a vendu le produit Netweaver et que vous, en tant qu'utilisateur, êtes maintenant victime d'une mauvaise programmation. Les testeurs sont également à blâmer car ils n'ont pas fait tous les tests aux limites.
randominstanceOfLivingThing du
1

Il y a quelques règles de gestion des exceptions que vous devez garder à l'esprit. Mais d'abord, vous devez vous rappeler que les exceptions font partie de l'interface exposée par le code; les documenter . Ceci est particulièrement important lorsque l'interface est publique, bien sûr, mais c'est également une très bonne idée dans les interfaces privées.

Les exceptions ne doivent être gérées qu'au point où le code peut faire quelque chose de sensé avec elles. La pire option de manipulation est de ne rien faire du tout à leur sujet, ce qui ne devrait être fait que lorsque c'est exactement la bonne option. (Lorsque j'ai une telle situation dans mon code, j'inclus un commentaire à cet effet afin que je sache ne pas m'inquiéter du corps vide.)

La deuxième pire option consiste à lever une exception sans rapport avec l'original attaché comme cause. Le problème ici est que les informations dans l'exception d'origine qui permettraient de diagnostiquer le problème sont perdues; vous créez quelque chose avec lequel personne ne peut rien faire (à part se plaindre que «cela ne fonctionne pas», et nous savons tous comment nous détestons ces rapports de bogues).

Il est beaucoup mieux de consigner l'exception. Cela permet à quelqu'un de découvrir le problème et de le résoudre, mais vous ne devez enregistrer l'exception qu'au point où elle serait sinon perdue ou signalée via une connexion externe. Ce n'est pas parce que la journalisation plus souvent est un problème majeur en tant que tel, mais plutôt parce qu'une journalisation excessive signifie que le journal consomme plus d'espace sans contenir plus d'informations. Une fois que vous avez enregistré l'exception, vous pouvez signaler une décision précise à l'utilisateur / client avec bonne conscience (tant que vous incluez également l'heure de génération - ou un autre identifiant de corrélation - dans ce rapport afin que la version courte puisse être mise en correspondance avec le détail si nécessaire).

La meilleure option est, bien sûr, de gérer complètement l'exception, en traitant la situation d'erreur dans son intégralité. Si vous pouvez le faire, faites-le! Cela peut même signifier que vous pouvez éviter d'avoir à enregistrer l'exception.

Une façon de gérer une exception consiste à lever une autre exception qui fournit une description de niveau supérieur du problème (par exemple, " failed to initialize" au lieu de " index out of bounds"). C'est un bon schéma tant que vous ne perdez pas les informations sur la cause de l'exception; utilisez l'exception détaillée pour initialiser l' causeexception de niveau supérieur ou enregistrez le détail (comme expliqué ci-dessus). La journalisation est plus appropriée lorsque vous êtes sur le point de traverser une frontière inter-processus, comme un appel IPC, car il n'y a aucune garantie que la classe d'exception de bas niveau sera présente du tout à l'autre extrémité de la connexion. Le maintien en tant que cause attachée est le plus approprié lors du franchissement d'une frontière interne.

Un autre modèle que vous voyez est le catch-and-release:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Il s'agit d'un anti-modèle, sauf si vous avez des contraintes de type provenant d'autres catchclauses, ce qui signifie que vous ne pouvez pas laisser passer l'exception par elle-même. Ensuite, ce n'est qu'une laideur de Java.

Il n'y a pas de réelle différence entre les exceptions vérifiées et celles non vérifiées, à part le fait que vous devez déclarer les exceptions vérifiées qui franchissent les limites des méthodes. C'est toujours une bonne idée de documenter les exceptions non contrôlées (avec le @throwscommentaire javadoc) si vous savez qu'elles sont délibérément levées par votre code. Ne jetez pas délibérément java.lang.Errorou ses sous-classes (sauf si vous écrivez une implémentation JVM).

Avis: Un cas d'erreur inattendue représente toujours un bug dans votre code. Les exceptions vérifiées sont un moyen de gérer cette menace, et lorsque les développeurs utilisent délibérément des exceptions non contrôlées pour éviter les problèmes de gestion des cas d'erreur, vous accumulez beaucoup de dettes techniques que vous devrez nettoyer un certain temps si vous voulez du code robuste. La gestion des erreurs bâclée n'est pas professionnelle (et regarder la gestion des erreurs est un bon moyen de déterminer à quel point un programmeur est vraiment bon).

Associés Donal
la source
0

Je pense que vous ne devez lever l'exception vérifiée que lorsque l'application peut récupérer. Ainsi, les utilisateurs de votre bibliothèque doivent intercepter ces exceptions et faire ce dont ils ont besoin pour récupérer. Tout le reste doit être décoché.

Par exemple,

Si votre application charge des données à partir d'un fichier ou d'une base de données. Ensuite,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

l'appelant peut toujours intercepter l'exception MissingDataFileException vérifiée, puis essayer de charger les données à partir de la base de données.

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}
Hendrix
la source
1
Saul est décédé dans un accident de voiture il y a 3 ans. Échouer! ;-)
Donal Fellows