La manière moderne de gérer les erreurs…

118

Cela fait un moment que je réfléchis à ce problème et je me trouve constamment en train de trouver des mises en garde et des contradictions. J'espère donc que quelqu'un pourra produire une conclusion sur ce qui suit:

Privilégier les exceptions aux codes d'erreur

Autant que je sache, après avoir travaillé dans l'industrie pendant quatre ans, lu des livres et des blogs, etc., la meilleure pratique actuelle en matière de gestion des erreurs consiste à lever des exceptions, plutôt que de renvoyer des codes d'erreur (pas nécessairement un code d'erreur, mais type représentant une erreur).

Mais pour moi cela semble contredire ...

Codage aux interfaces, pas aux implémentations

Nous codons pour des interfaces ou des abstractions afin de réduire le couplage. Nous ne savons pas ou ne voulons pas savoir le type et la mise en œuvre spécifiques d'une interface. Alors, comment pouvons-nous savoir quelles exceptions nous devrions chercher à attraper? L'implémentation peut générer 10 exceptions différentes, ou aucune. Lorsque nous détectons une exception, nous faisons sûrement des hypothèses sur la mise en œuvre.

Sauf si - l'interface a ...

Spécifications d'exception

Certains langages permettent aux développeurs d'indiquer que certaines méthodes génèrent certaines exceptions (Java, par exemple, utilise le throwsmot - clé.) Du point de vue du code appelant, cela semble très bien - nous savons explicitement quelles exceptions nous devrions éventuellement intercepter.

Mais - cela semble suggérer un ...

Abstraction qui fuit

Pourquoi une interface devrait-elle spécifier quelles exceptions peuvent être levées? Que se passe-t-il si l'implémentation n'a pas besoin de lancer une exception ou a besoin de lancer d'autres exceptions? Il n’ya aucun moyen, au niveau de l’interface, de savoir quelles exceptions une implémentation peut vouloir déclencher.

Alors...

De conclure

Pourquoi les exceptions sont-elles préférées quand elles semblent (à mes yeux) contredire les meilleures pratiques en matière de logiciels? Et, si les codes d'erreur sont si mauvais (et que je n'ai pas besoin d'être vendu sur les vices de codes d'erreur), existe-t-il une autre alternative? Quel est l'état actuel (ou à venir) de la gestion des erreurs qui répond aux exigences des meilleures pratiques décrites ci-dessus, mais ne repose pas sur un code d'appel vérifiant la valeur de retour des codes d'erreur?

RichK
la source
12
Je ne comprends pas votre argument sur les abstractions qui fuient. Spécifier quelle exception une méthode particulière peut générer fait partie de la spécification d'interface . Tout comme le type de la valeur de retour fait partie de la spécification d'interface. Les exceptions ne "sortent pas du côté gauche" de la fonction, mais font toujours partie de l'interface.
Déceze
@deceze Comment une interface peut-elle éventuellement indiquer ce qu'une implémentation peut générer? Il peut jeter toutes les exceptions dans le système de types! Et le fait que beaucoup de langues ne supportent pas les spécifications d'exception donnent à penser qu'elles sont plutôt
douteuses
1
Je conviens qu'il peut être difficile de gérer toutes les différentes exceptions qu'une méthode peut générer si elle utilise elle-même d'autres méthodes et ne détecte pas leurs exceptions en interne. Cela dit, ce n'est pas une contradiction.
Déceze
1
L'hypothèse qui se dégage est que le code peut gérer une exception lorsqu'il échappe à la limite de l'interface. C'est très peu probable, étant donné qu'il n'en sait pas assez sur ce qui s'est réellement passé. Tout ce qu'il sait, c'est que quelque chose s'est mal passé et que la mise en œuvre de l'interface ne savait pas comment y remédier. Les chances qu'il puisse faire un meilleur travail sont faibles, au-delà de le signaler et de mettre fin au programme. Ou l'ignorer si l'interface n'est pas essentielle au bon fonctionnement du programme. Un détail d'implémentation que vous ne pouvez ni ne devriez encoder dans un contrat d'interface.
Hans Passant

Réponses:

31

Tout d'abord, je ne suis pas d'accord avec cette affirmation:

Privilégier les exceptions aux codes d'erreur

Ce n'est pas toujours le cas: par exemple, jetez un coup d'œil à Objective-C (avec le framework Foundation). Là, NSError est le moyen privilégié pour gérer les erreurs, malgré l'existence de ce qu'un développeur Java appellerait de véritables exceptions: @try, @catch, @throw, la classe NSException, etc.

Cependant, il est vrai que de nombreuses interfaces perdent leurs abstractions avec les exceptions levées. J’estime que ce n’est pas la faute du style "exception" de propagation / traitement des erreurs. En général, je crois que le meilleur conseil concernant la gestion des erreurs est le suivant:

Traiter l'erreur / l'exception au niveau le plus bas possible, période

Je pense que si on s'en tient à cette règle générale, la quantité de "fuites" d'abstractions peut être très limitée et contenue.

Pour savoir si les exceptions levées par une méthode devraient faire partie de sa déclaration, je pense qu’elles devraient: elles font partie du contrat défini par cette interface: Cette méthode effectue A, ou échoue avec B ou C.

Par exemple, si une classe est un analyseur XML, une partie de sa conception devrait indiquer que le fichier XML fourni est tout simplement faux. En Java, vous le faites normalement en déclarant les exceptions que vous vous attendez à rencontrer et en les ajoutant à la throwspartie de la déclaration de la méthode. D'autre part, si l'un des algorithmes d'analyse échoue, il n'y a aucune raison de passer cette exception ci-dessus non gérée.

Tout se résume à une chose: une bonne conception d'interface. Si vous concevez votre interface suffisamment bien, aucune exception ne devrait vous hanter. Sinon, ce ne sont pas que des exceptions qui vous dérangeraient.

De plus, je pense que les créateurs de Java avaient de très fortes raisons de sécurité pour inclure des exceptions à une déclaration / définition de méthode.

Une dernière chose: certaines langues, par exemple Eiffel, disposent d'autres mécanismes de traitement des erreurs et n'incluent tout simplement pas les capacités de projection. Là, une "exception" de type est automatiquement déclenchée lorsqu'une post-condition pour une routine n'est pas satisfaite.

K.Steff
la source
12
+1 pour nier "Favoriser les exceptions aux codes d'erreur." On m'a appris que les exceptions sont bonnes et bonnes, dans la mesure où elles sont, en fait, des exceptions et non la règle. L'appel d'une méthode qui lève une exception pour déterminer si une condition est vraie est une très mauvaise pratique.
Neil
4
@ JoshuaDrake: Il a définitivement tort. Les exceptions et gotosont très différentes. Par exemple, les exceptions vont toujours dans le même sens, en aval de la pile d'appels. Deuxièmement, vous protéger des exceptions surprises est exactement la même chose que DRY - par exemple, en C ++, si vous utilisez RAII pour assurer le nettoyage, cela garantit le nettoyage dans tous les cas, pas seulement les exceptions, mais également tous les flux de contrôle normaux. C'est infiniment plus fiable. try/finallyaccomplit quelque chose de semblable. Lorsque vous garantissez correctement le nettoyage, vous n'avez pas besoin de considérer les exceptions comme un cas particulier.
DeadMG
4
@ JoshuaDrake Désolé, mais Joel est loin de là. Les exceptions ne sont pas les mêmes que goto, vous montez toujours au moins un niveau dans la pile d'appels. Et que faites-vous si le niveau ci-dessus ne peut pas traiter immédiatement le code d'erreur? Renvoyer encore un autre code d'erreur? Une partie du problème avec les erreurs est qu’elles PEUVENT être ignorées, ce qui entraîne des problèmes plus graves que le lancement d’une exception.
Andy
25
-1 parce que je suis complètement en désaccord avec "Traite l'erreur / exception au niveau le plus bas possible" - c'est faux, tout simplement. Traitez les erreurs / exceptions au niveau approprié . Très souvent, il s'agit d'un niveau beaucoup plus élevé et, dans ce cas, les codes d'erreur sont très pénibles. La raison pour préférer les exceptions aux codes d'erreur est qu'elles vous permettent de choisir librement à quel niveau les traiter sans impacter les niveaux intermédiaires.
Michael Borgwardt
2
@Giorgio: voir artima.com/intv/handcuffs.html - en particulier les pages 2 et 3.
Michael Borgwardt
27

Je voudrais simplement noter que les exceptions et les codes d'erreur ne sont pas le seul moyen de traiter les erreurs et les chemins de code alternatifs.

Tout en haut de l’esprit, vous pouvez avoir une approche semblable à celle adoptée par Haskell, où les erreurs peuvent être signalées via des types de données abstraits avec plusieurs constructeurs (pensez des discriminations discriminées, ou des pointeurs nuls, mais avec une sécurité typographique et avec la possibilité d’ajouter une syntaxe. fonctions sucre ou aide pour rendre le flux de code bien paraître).

func x = do
    a <- operationThatMightFail 10
    b <- operationThatMightFail 20
    c <- operationThatMightFail 30
    return (a + b + c)

operationThatMightfail est une fonction qui retourne une valeur encapsulée dans un Maybe. Cela fonctionne comme un pointeur nullable, mais la notation do garantit que le tout est évalué à null si l'un de a, b ou c échoue. (et le compilateur vous empêche de créer une exception NullPointerException accidentelle)

Une autre possibilité consiste à transmettre un objet de gestionnaire d'erreur en tant qu'argument supplémentaire à chaque fonction que vous appelez. Ce gestionnaire d'erreurs a une méthode pour chaque "exception" possible qui peut être signalée par la fonction à laquelle vous le transmettez, et peut être utilisée par cette fonction pour traiter les exceptions où elles se produisent, sans nécessairement devoir rembobiner la pile via des exceptions.

Common LISP fait cela, et le rend possible grâce à un support syntaxique (arguments implicites) et à ce que les fonctions intégrées suivent ce protocole.

hugomg
la source
1
C'est vraiment chouette. Merci d'avoir répondu à la partie sur les alternatives :)
RichK
1
BTW, exceptions est l'une des parties les plus fonctionnelles des langages impératifs modernes. Les exceptions forment une monade. Les exceptions utilisent la correspondance de patter. Les exceptions permettent d'imiter le style applicatif sans apprendre réellement ce qui Maybeest.
9000
Je lisais un livre sur SML récemment. Il mentionnait les types d'options, les exceptions (et les continuations). Le conseil était d'utiliser des types d'options lorsque le cas non défini devait se produire assez souvent et d'utiliser des exceptions lorsque le cas non défini se produisait très rarement.
Giorgio
8

Oui, les exceptions peuvent provoquer des abstractions qui fuient. Mais les codes d'erreur ne sont-ils pas encore pires à cet égard?

Une façon de résoudre ce problème consiste à faire en sorte que l'interface spécifie exactement les exceptions pouvant être levées dans quelles circonstances et déclare que les implémentations doivent mapper leur modèle d'exception interne sur cette spécification, en interceptant, en convertissant et en rediffusant les exceptions si nécessaire. Si vous voulez une interface "préfet", c'est la voie à suivre.

En pratique, il suffit généralement de spécifier des exceptions qui font logiquement partie de l'interface et qu'un client peut vouloir intercepter et faire quelque chose. Il est généralement admis qu'il peut exister d'autres exceptions lorsque des erreurs de bas niveau surviennent ou qu'un manifeste de bogue, et qu'un client ne peut généralement les gérer qu'en affichant un message d'erreur et / ou en arrêtant l'application. Au moins, l'exception peut toujours contenir des informations permettant de diagnostiquer le problème.

En fait, avec les codes d'erreur, pratiquement la même chose finit par se produire, mais de manière plus implicite, et avec beaucoup plus de risques de perte d'informations et d'application dans un état incohérent.

Michael Borgwardt
la source
1
Je ne comprends pas pourquoi un code d'erreur peut provoquer une abstraction qui fuit. Si un code d'erreur est renvoyé à la place d'une valeur de retour normale et que ce comportement est décrit dans la spécification de la fonction / méthode, il n'y a pas de fuite IMO. Ou est-ce que je néglige quelque chose?
Giorgio
Si le code d'erreur est spécifique à l'implémentation alors que l'API est supposée être indépendante de l'implémentation, spécifier et renvoyer le code d'erreur entraîne la divulgation de détails d'implémentation indésirables. Exemple typique: une API de journalisation avec une implémentation basée sur fichier et sur une base de données, où la première peut avoir une erreur «disque plein» et la dernière une erreur «connexion à la base de données refusée par l'hôte».
Michael Borgwardt
Je comprends ce que tu veux dire. Si vous souhaitez signaler des erreurs spécifiques à une implémentation, l'API ne peut pas être agnostique pour l'implémentation (ni avec des codes d'erreur ni avec des exceptions). Je suppose que la seule solution serait de définir un code d'erreur indépendant de l'implémentation, tel que "ressource non disponible", ou de décider que l'API n'est pas indépendante de l'implémentation.
Giorgio
1
@Giorgio: Oui, la génération de rapports d'erreurs spécifiques à l'implémentation est délicate avec une API indépendante de l'implémentation. Néanmoins, vous pouvez le faire avec des exceptions, car (contrairement aux codes d'erreur), ils peuvent avoir plusieurs champs. Vous pouvez donc utiliser le type de l'exception pour fournir les informations d'erreur génériques (ResourceMissingException) et inclure un code / message d'erreur spécifique à l'implémentation en tant que champ. Le meilleur des deux mondes :-).
Sleske
Et BTW, c’est exactement ce que fait java.lang.SQLException. Il a getSQLState(générique) et getErrorCode(spécifique au vendeur). Maintenant, si seulement il avait des sous-classes appropriées ...
Sleske
5

Beaucoup de bonnes choses ici, je voudrais juste ajouter que nous devrions tous nous méfier du code qui utilise des exceptions dans le cadre du flux de contrôle normal. Parfois, les gens tombent dans ce piège où tout ce qui n'est pas le cas habituel devient une exception. J'ai même vu une exception utilisée comme condition de terminaison de boucle.

Les exceptions signifient "que quelque chose que je ne peux pas gérer ici s'est produit, il est nécessaire de contacter quelqu'un d'autre pour savoir quoi faire." Un utilisateur qui saisit une entrée non valide n'est pas une exception (qui devrait être traitée localement par l'entrée en demandant à nouveau, etc.).

Un autre cas dégénéré d'utilisation des exceptions que j'ai rencontré concerne des personnes dont la première réponse est "déclenche une exception". Ceci est presque toujours fait sans écrire la capture (règle de base: écrivez la capture en premier, puis la déclaration de projection). Dans les grandes applications, cela devient problématique lorsqu'une exception non capturée bouillonne dans les régions inférieures et fait exploser le programme.

Je ne suis pas anti-exceptions, mais elles semblent être des singletons d'il y a quelques années: utilisées beaucoup trop fréquemment et de manière inappropriée. Ils sont parfaits pour l'usage auquel ils sont destinés, mais cette affaire n'est pas aussi vaste que certains le pensent.

anon
la source
4

Abstraction qui fuit

Pourquoi une interface devrait-elle spécifier quelles exceptions peuvent être levées? Que se passe-t-il si l'implémentation n'a pas besoin de lancer une exception ou a besoin de lancer d'autres exceptions? Il n’ya aucun moyen, au niveau de l’interface, de savoir quelles exceptions une implémentation peut vouloir déclencher.

Nan. Les spécifications d'exception se trouvent dans le même compartiment que les types de retour et d'argument. Elles font partie de l'interface. Si vous ne pouvez pas vous conformer à cette spécification, n'implémentez pas l'interface. Si vous ne lancez jamais, c'est bien. Il n'y a rien de fuyant dans la spécification d'exceptions dans une interface.

Les codes d'erreur sont plus que mauvais. Ils sont terribles. Vous devez vous rappeler manuellement de les vérifier et de les propager, chaque fois, pour chaque appel. Cela viole DRY, pour commencer, et fait exploser votre code de traitement des erreurs. Cette répétition est un problème bien plus important que celui rencontré par les exceptions. Vous ne pouvez jamais ignorer une exception en silence, mais les utilisateurs peuvent et le font en silence pour ignorer les codes de retour, ce qui est définitivement une mauvaise chose.

DeadMG
la source
Les codes d'erreur peuvent être plus faciles à utiliser si vous avez de bonnes formes de sucre syntaxique ou de méthodes auxiliaires, et dans certaines langues, vous pouvez faire en sorte que le compilateur et le système de types garantissent que vous n'oublierez jamais de gérer un code d'erreur. En ce qui concerne la partie interface d’exception, je pense qu’il pensait au fameux clunkyness des exceptions vérifiées de Java. Bien qu’elles semblent à première vue une idée parfaitement raisonnable, elles posent de nombreux problèmes pénibles dans la pratique.
Hugomg
@missingno: C'est parce que, comme d'habitude, Java a une implémentation terrible, pas parce que les exceptions vérifiées sont intrinsèquement mauvaises.
DeadMG
1
@missingno: Quels types de problèmes peuvent être causés par des exceptions vérifiées?
Giorgio
@Giorgio: Il est très difficile de prévoir toutes les exceptions qu'une méthode peut générer, car cela doit prendre en compte d'autres sous-classes et du code qui n'a pas encore été écrit. En pratique, les utilisateurs finissent par trouver des solutions de contournement qui jettent des informations, telles que la réutilisation d'une classe d'exceptions système pour tout, ou l'obligation de capturer et de rejeter fréquemment des exceptions internes. J'ai aussi entendu dire que les exceptions vérifiées constituaient un gros obstacle quand ils essayaient d'ajouter des fonctions anonymes à la langue.
Hugomg
@missingno: Les fonctions AFAIK anonymes en Java ne sont que du sucre syntaxique pour les classes internes anonymes avec exactement une méthode. Je ne suis donc pas sûr de comprendre pourquoi les exceptions cochées seraient un problème (mais j'avoue que je ne connais pas grand chose à ce sujet). Oui, il est difficile de prévoir les exceptions qu'une méthode générera. C’est pourquoi, il est utile d’avoir des exceptions vérifiées afin que vous n’ayez pas à deviner. Bien sûr, vous pouvez écrire du code de mauvaise qualité pour les gérer, mais vous pouvez également le faire avec des exceptions non vérifiées. Cependant, je sais que le débat est assez complexe et honnêtement, je vois les avantages et les inconvénients des deux côtés.
Giorgio
2

Bien La gestion des exceptions peut avoir sa propre implémentation d'interface. Selon le type de l'exception levée, effectuez les étapes souhaitées.

La solution à votre problème de conception consiste à avoir deux implémentations d'interface / abstraction. Un pour la fonctionnalité et l'autre pour la gestion des exceptions. Et en fonction du type de l'exception interceptée, appelez la classe de type d'exception appropriée.

L'implémentation de codes d'erreur est un moyen orthodoxe de gérer les exceptions. C'est comme l'utilisation de chaîne vs. constructeur de chaînes.

Estefany Velez
la source
4
autrement dit: les implémentations jettent des sous-classes des exceptions définies dans l'api.
Andrew Cooke
2

Les exceptions IM-very-HO doivent être évaluées au cas par cas, car en interrompant le flux de contrôle, elles augmenteront inutilement la complexité réelle et perçue de votre code. Mettez de côté la discussion relative au lancement d'exceptions dans vos fonctions - ce qui peut réellement améliorer votre flux de contrôle. Si vous envisagez de lancer des exceptions via des limites d'appels, tenez compte des points suivants:

Permettre à un appelé de rompre votre flux de contrôle peut ne pas fournir d'avantage réel et il peut ne pas y avoir de moyen significatif de traiter l'exception. Par exemple, si l’on applique le modèle Observable (dans un langage tel que C # dans lequel vous avez des événements partout et que throwsla définition n’est pas explicite ), il n’ya aucune raison réelle de laisser l’observateur interrompre votre flux de contrôle s’il se bloque et aucun moyen significatif de gérer leurs affaires (bien sûr, un bon voisin ne devrait pas jeter en observant, mais personne n'est parfait).

L'observation ci-dessus peut être étendue à toute interface faiblement couplée (comme vous l'avez indiqué); Je pense que c'est en fait une norme qu'après avoir remonté 3-6 cadres de pile, une exception non capturée est susceptible de se retrouver dans une section de code qui soit:

  • est trop abstrait pour traiter l’exception de manière significative, même si l’exception elle-même est upcastée;
  • exécute une fonction générique (ne vous souciez pas de savoir pourquoi vous avez échoué, comme un message pompe ou l'observable);
  • est spécifique, mais avec une responsabilité différente, et cela ne devrait vraiment pas vous inquiéter;

Compte tenu de ce qui précède, la décoration des interfaces avec la throwssémantique n’est qu’un gain fonctionnel marginal, car de nombreux appelants par le biais de contrats d’interface ne se soucieraient que de votre éventuel échec.

Je dirais que cela devient alors une question de goût et de commodité: votre objectif principal est de récupérer votre état à la fois dans l'appelant et l'appelant après une "exception". Par conséquent, si vous avez beaucoup d'expérience dans le déplacement des codes d'erreur (à venir), C, ou si vous travaillez dans un environnement où les exceptions peuvent tourner au diable (C ++), je ne crois pas que le fait de jeter des choses est si important pour une bonne OOP propre que vous ne pouvez pas compter sur votre ancien. motifs si vous êtes mal à l'aise avec elle. Surtout si cela conduit à casser le SoC.

D'un point de vue théorique, je pense qu'une méthode so-kasher de gestion des exceptions peut être dérivée directement du constat que la plupart du temps, l'appelant direct ne se soucie que de ce que vous avez échoué, pas pourquoi. L'appelé jette, une personne très proche au-dessus (2-3 images) intercepte une version superposée, et l'exception réelle est toujours affectée par un gestionnaire d'erreur spécialisé (même s'il ne s'agit que de traçage) - c'est là que l'AOP serait utile, car ces gestionnaires sont susceptibles d'être horizontaux.

vski
la source
1

Privilégier les exceptions aux codes d'erreur

  • Les deux devraient coexister.

  • Renvoie le code d'erreur lorsque vous anticipez un comportement donné.

  • Renvoie une exception lorsque vous n'avez pas anticipé certains comportements.

  • Les codes d'erreur sont normalement associés à un seul message, quand le type d'exception reste, mais un message peut varier

  • L'exception a une trace de pile, quand le code d'erreur ne le fait pas. Je n'utilise pas de codes d'erreur pour déboguer un système défectueux.

Codage pour les interfaces et non pour les implémentations

Cela peut être spécifique à JAVA, mais lorsque je déclare mes interfaces, je ne précise pas quelles exceptions peuvent être levées par une implémentation de cette interface, cela n'a aucun sens.

Lorsque nous détectons une exception, nous faisons sûrement des hypothèses sur la mise en œuvre.

Ceci est entièrement à vous. Vous pouvez essayer d’attraper un type d’exception très spécifique, puis un type plus général Exception. Pourquoi ne pas laisser exception propager la pile et la gérer? Sinon, vous pouvez regarder la programmation d'aspects où la gestion des exceptions devient un aspect "enfichable".

Que se passe-t-il si l'implémentation n'a pas besoin de lancer une exception ou a besoin de lancer d'autres exceptions?

Je ne comprends pas pourquoi est-ce un problème pour vous? Oui, vous pouvez avoir une implémentation qui n'échoue jamais ou lève des exceptions et vous pouvez avoir une implémentation différente qui échoue et lève constamment des exceptions. Si c'est le cas, ne spécifiez aucune exception sur l'interface et votre problème est résolu.

Cela changerait-il quelque chose si, au lieu d'exception, votre implémentation renvoyait un objet résultat? Cet objet contiendrait le résultat de votre action ainsi que les éventuelles erreurs / échecs. Vous pouvez ensuite interroger cet objet.

CodeART
la source
1

Abstraction qui fuit

Pourquoi une interface devrait-elle spécifier quelles exceptions peuvent être levées? Que se passe-t-il si l'implémentation n'a pas besoin de lancer une exception ou a besoin de lancer d'autres exceptions? Il n’ya aucun moyen, au niveau de l’interface, de savoir quelles exceptions une implémentation peut vouloir déclencher.

D'après mon expérience, le code qui reçoit l'erreur (que ce soit via une exception, un code d'erreur ou quoi que ce soit d'autre) ne se souciera normalement pas de la cause exacte de l'erreur - il réagirait de la même manière en cas d'échec, sauf en cas de signalement éventuel de l'erreur. error (que ce soit un dialogue d'erreur ou une sorte de journal); et ce rapport serait fait orthogonalement au code qui a appelé la procédure défaillante. Par exemple, ce code pourrait transmettre l'erreur à un autre élément de code sachant comment signaler des erreurs spécifiques (par exemple, formater une chaîne de message), éventuellement en joignant des informations de contexte.

Bien sûr, dans certains cas, il est nécessaire d’attacher une sémantique spécifique aux erreurs et de réagir différemment en fonction de l’erreur survenue. Ces cas doivent être documentés dans la spécification d'interface. Cependant, l'interface peut toujours se réserver le droit de lancer d'autres exceptions sans signification spécifique.

Ambroz Bizjak
la source
1

Je trouve que les exceptions permettent d’écrire un code plus structuré et concis pour signaler et traiter les erreurs: l’utilisation de codes d’erreur nécessite de vérifier les valeurs de retour après chaque appel et de décider quoi faire en cas de résultat inattendu.

D'autre part, je conviens que les exceptions révèlent des détails d'implémentation qui doivent être cachés dans le code appelant une interface. Puisqu'il n'est pas possible de savoir a priori quel morceau de code peut lancer quelles exceptions (à moins qu'elles ne soient déclarées dans la signature de la méthode comme en Java), en utilisant des exceptions, nous introduisons des dépendances implicites très complexes entre différentes parties du code, ce qui contre le principe de minimisation des dépendances.

Résumant:

  • Je pense que les exceptions permettent un code plus propre et une approche plus agressive des tests et du débogage, car les exceptions non capturées sont beaucoup plus visibles et difficiles à ignorer que les codes d'erreur (échec prochain).
  • D'autre part, des bogues d'exception non capturés qui ne sont pas découverts lors des tests peuvent apparaître dans un environnement de production sous la forme d'un crash. Dans certaines circonstances, ce comportement n'est pas acceptable et, dans ce cas, l'utilisation de codes d'erreur est une approche plus robuste.
Giorgio
la source
1
Je ne suis pas d'accord. Oui, les exceptions non capturées peuvent provoquer le blocage d’une application, mais les codes d’erreur non vérifiés le peuvent également - c’est donc un lavage. Si vous utilisez correctement les exceptions, les seules non-capturées seront celles qui sont fatales (comme OutOfMemory), et pour celles-ci, écraser immédiatement est le mieux que vous puissiez faire.
Sleske
Le code d'erreur fait partie du contrat entre l'appelant m1 et l'appelé m2: les codes d'erreur possibles sont définis dans l'interface de m2 uniquement. Avec des exceptions (sauf si vous utilisez Java et que vous déclarez toutes les exceptions levées dans les signatures de méthode), vous avez un contrat implicite entre l'appelant m1 et toutes les méthodes pouvant être appelées par m2, de manière récursive. Donc, bien sûr, c’est une erreur de ne pas vérifier le code d’erreur renvoyé, mais il est toujours possible de le faire. Par contre, il n’est pas toujours possible de vérifier toutes les exceptions levées par une méthode, à moins que vous ne sachiez comment elle est implémentée.
Giorgio
Premièrement: Vous pouvez vérifier toutes les exceptions - il vous suffit de faire "Gestion des exceptions Pokemon" (il faut les attraper toutes - c'est-à-dire catch Exceptionou même Throwable, ou équivalent).
Sleske
1
En pratique, si l'API est correctement conçue, elle spécifiera toutes les exceptions que le client peut gérer de manière significative - elles doivent être interceptées spécifiquement. Ce sont les équivalents des codes d'erreur). Toute autre exception signifie "erreur interne" et l'application devra être arrêtée, ou du moins arrêtée, du sous-système correspondant. Vous pouvez les attraper si vous voulez (voir ci-dessus), mais vous devriez généralement les laisser bouillonner. Ce "bouillonnement" est le principal avantage de l'utilisation des exceptions. Vous pouvez toujours les rattraper plus loin, ou pas, selon les besoins.
Sleske
-1

Soit biaisé à droite.

On ne peut pas l'ignorer, il faut le gérer, c'est complètement transparent. Et si vous utilisez le type d'erreur correct pour gaucher, il transmet les mêmes informations qu'une exception Java.

Inconvénient? Le code avec une gestion correcte des erreurs semble dégoûtant (vrai de tous les mécanismes).

Keynan
la source
cela ne semble rien offrir de substantiel par rapport aux points soulevés et expliqués dans les 10 réponses précédentes. Pourquoi cogner une question de deux ans avec des trucs comme celui-ci?
Gnat
Sauf que personne ici n'a mentionné la droite biaisée non plus. hugomg a commencé à parler de haskell, mais peut-être que c'est un gestionnaire d'erreurs de merde car il ne laisse aucune explication sur la raison de l'erreur, ni aucune méthode directe permettant de récupérer, et les rappels sont l'un des plus grands péchés de la conception de flux de contrôle. Et cette question est venue sur google.
Keynan