Quand lever une exception?

435

J'ai créé des exceptions pour chaque condition à laquelle ma candidature ne s'attend pas. UserNameNotValidException, PasswordNotCorrectExceptionetc.

Cependant, on m'a dit de ne pas créer d'exceptions à ces conditions. Dans mon UML, ce sont des exceptions au flux principal, alors pourquoi ne devrait-il pas s'agir d'une exception?

Des conseils ou des meilleures pratiques pour créer des exceptions?

Kwan Cheng
la source
59
Veuillez rouvrir, c'est une question très sensée et valide. Toute question implique une certaine opinion, mais dans ce cas, je soupçonne que c'est une question de «meilleure pratique».
Tim Long
19
+1 pour la réouverture. Comme beaucoup d'autres sujets intéressants, «cela dépend» et il est très utile d'analyser les compromis lors de la prise de décisions. Le fait que les gens confondent les opinions et les faits dans les réponses ne nie pas cela. Passer au crible la boue est un exercice qui doit être laissé au lecteur.
aron
12
Je conviens également que cette question devrait être rouverte car elle est liée aux meilleures pratiques. Soit dit en passant, les meilleures pratiques sont toujours des opinions qui peuvent aider les autres.
Ajay Sharma
6
Microsoft dit: "Ne renvoyez pas de codes d'erreur. Les exceptions sont le principal moyen de signaler les erreurs dans les frameworks." et "... Si un membre ne parvient pas à faire ce pour quoi il est conçu, cela doit être considéré comme un échec d'exécution et une exception doit être levée.". msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75
4
Celles-ci pourraient être une exception totalement raisonnable, cela dépend simplement des méthodes qui les lancent. Une méthode appelée IsCredentialsValid(username,password)ne doit pas lever d'exception si le nom d'utilisateur ou le mot de passe n'est pas valide, mais renvoie false. Mais disons qu'une méthode qui lit les données de la base de données pourrait légitimement lever une telle exception, si l'authentification échoue. En bref: vous devez lever une exception si une méthode n'est pas en mesure d'effectuer la tâche qu'elle est censée effectuer.
JacquesB

Réponses:

629

Ma directive personnelle est la suivante: une exception est levée lorsqu'une supposition fondamentale du bloc de code actuel se révèle fausse.

Exemple 1: disons que j'ai une fonction qui est censée examiner une classe arbitraire et retourner true si cette classe hérite de List <>. Cette fonction pose la question: "Cet objet est-il un descendant de List?" Cette fonction ne doit jamais lever d'exception, car il n'y a pas de zones grises dans son fonctionnement - chaque classe hérite ou n'hérite pas de List <>, donc la réponse est toujours "oui" ou "non".

Exemple 2: disons que j'ai une autre fonction qui examine une liste <> et renvoie vrai si sa longueur est supérieure à 50, et faux si la longueur est inférieure. Cette fonction pose la question "Cette liste contient-elle plus de 50 éléments?" Mais cette question fait une hypothèse - elle suppose que l'objet qui lui est donné est une liste. Si je lui donne un NULL, cette hypothèse est fausse. Dans ce cas, si la fonction retourne soit vrai ou faux, il est alors briser ses propres règles. La fonction ne peut retourner rien et prétendre qu'il a répondu correctement à la question. Donc, il ne revient pas - il lève une exception.

Ceci est comparable à l' erreur logique de «question chargée» . Chaque fonction pose une question. Si l'entrée qui lui est donnée fait de cette question une erreur, lancez une exception. Cette ligne est plus difficile à tracer avec des fonctions qui renvoient void, mais la ligne de fond est la suivante: si les hypothèses de la fonction sur ses entrées sont violées, elle devrait lever une exception au lieu de revenir normalement.

L'autre côté de cette équation est: si vous trouvez que vos fonctions lèvent fréquemment des exceptions, alors vous devrez probablement affiner leurs hypothèses.

The Digital Gabeg
la source
15
Exactement! Une exception est levée lorsque et seulement lorsque les conditions préalables de la fonction (hypothèses sur les arguments) sont rompues !
Lightman
11
En linguistique, cela est parfois appelé échec de présupposition . L'exemple classique est dû à Bertrand Russell: "Le roi de France est-il chauve" ne peut pas être répondu par oui ou par non (resp. "Le roi de France est chauve" n'est ni vrai ni faux), car il contient une fausse présupposition , à savoir qu'il y a un roi de France. L'échec de la présupposition est souvent vu avec des descriptions précises, et c'est courant lors de la programmation. Par exemple, "La tête d'une liste" a un échec de présupposition lorsqu'une liste est vide, puis il est approprié de lever une exception.
Mohan
C'est peut-être la meilleure explication!
gaurav
Je vous remercie. Donc. Beaucoup. "Le roi de France est-il chauve". J'ai déjà entendu cela lors de mes recherches dans la jungle de meinong .... :) Merci. @Mohan
ErlichBachman
285

Parce que ce sont des choses qui se passeront normalement. Les exceptions ne sont pas des mécanismes de flux de contrôle. Les utilisateurs se trompent souvent de mot de passe, ce n'est pas un cas exceptionnel. Les exceptions devraient être une chose vraiment rare, UserHasDiedAtKeyboarddes situations de type.

blowdart
la source
3
Hmm, non. Des exceptions peuvent être utilisées comme mécanismes de flux de contrôle si des performances maximales ne sont pas requises, ce qui est vrai de la plupart des applications Web. Python utilise l'exception «StopIteration» pour terminer les itérateurs, et cela fonctionne très bien. Le coût n'est rien comparé à IO, etc.
Seun Osewa
9
+1 excellente réponse. Je suis tellement frustré par les développeurs travaillant sur les API que je dois consommer et lever des exceptions pour chaque petite chose. TRÈS peu de cas nécessitent vraiment des exceptions. Si vous avez défini 25 types d'exceptions différents, examinez à nouveau votre conception, vous vous trompez peut-être.
7wp
1
devrait-il y avoir une exception lorsque l'utilisateur essaie une action illégale à laquelle il n'est pas autorisé en manipulant le code de la page Web, par exemple en supprimant les publications de quelqu'un d'autre ici sur StackOverflow?
Rajat Gupta
30
Les exceptions SONT les mécanismes de contrôle des flux. Vous pouvez les jeter. Vous pouvez les attraper. Vous avez déplacé le contrôle vers un autre morceau de code. C'est le flux de contrôle. La seule raison pour laquelle les langues ont des exceptions est que vous pouvez écrire du code simple sans demander "cette chose a-t-elle échoué?" après tout ce que vous faites. Haskell, par exemple, n'a pas d'exceptions car les monades et la notation do peuvent automatiser la vérification des erreurs pour vous.
Jesse
2
Les exceptions sont plus que des mécanismes de contrôle des flux. Ils fournissent au (méthode) client des informations utiles sur les résultats exceptionnels dont ils doivent être conscients et manipulés. Autrement dit, les exceptions utilisées correctement rendent les API plus robustes
idelvall
67

Mes petites lignes directrices sont fortement influencées par le grand livre "Code complet":

  • Utilisez des exceptions pour notifier les choses à ne pas ignorer.
  • N'utilisez pas d'exceptions si l'erreur peut être gérée localement
  • Assurez-vous que les exceptions sont au même niveau d'abstraction que le reste de votre routine.
  • Des exceptions devraient être réservées à ce qui est vraiment exceptionnel .
Commander Keen
la source
35

Ce n'est PAS une exception si le nom d'utilisateur n'est pas valide ou si le mot de passe n'est pas correct. Ce sont des choses auxquelles vous devez vous attendre dans le cours normal des opérations. Les exceptions sont des choses qui ne font pas partie du fonctionnement normal du programme et sont plutôt rares.

EDIT: Je n'aime pas utiliser les exceptions car vous ne pouvez pas dire si une méthode lève une exception simplement en regardant l'appel. C'est pourquoi les exceptions ne devraient être utilisées que si vous ne pouvez pas gérer la situation de manière décente (pensez "mémoire insuffisante" ou "ordinateur en feu").

EricSchaefer
la source
"Je n'aime pas utiliser les exceptions car vous ne pouvez pas savoir si une méthode lève une exception simplement en regardant l'appel." C'est pourquoi il existe des exceptions vérifiées pour les langues qui les prennent en charge.
Newtopian
1
Les exceptions cochées ont leur propre ensemble de problèmes. Je préfère toujours utiliser des exceptions de «circonstances exceptionnelles», pas pour des choses qui font partie du flux de travail normal.
EricSchaefer
2
En réponse à votre montage. Je mets toujours dans mes documents xml à la fin de la section récapitulative les exceptions que la fonction lève pour que je puisse voir ces informations dans intellisense.
Matthew Vines,
1
devrait-il y avoir une exception lorsque l'utilisateur essaie une action illégale à laquelle il n'est pas autorisé en manipulant le code de la page Web, par exemple en supprimant les publications de quelqu'un d'autre ici sur StackOverflow?
Rajat Gupta
1
Il semble que vous parliez de la gestion des erreurs (notre mémoire, ordinateur en feu) par rapport à la gestion des exceptions de code (enregistrement manquant, type d'entrée non valide, etc.). Je pense qu'il y a une différence nette entre les deux.
Chuck Burgess
28

Une règle d'or consiste à utiliser des exceptions dans le cas de quelque chose que vous ne pourriez pas normalement prévoir. Les exemples sont la connectivité à la base de données, le fichier manquant sur le disque, etc. Pour les scénarios que vous pouvez prévoir, c'est-à-dire les utilisateurs qui tentent de se connecter avec un mauvais mot de passe, vous devez utiliser des fonctions qui renvoient des booléens et savoir comment gérer la situation avec élégance. Vous ne voulez pas interrompre brutalement l'exécution en lançant une exception simplement parce que quelqu'un a mal saisi son mot de passe.

japollock
la source
6
vous n'avez pas besoin d'arrêter l'exécution du programme sur une exception ... lancez l'exception, l'appelant intercepte alors l'exception et devrait, si possible, gérer et enregistrer les erreurs et continuer. Il est «mauvais pour la forme» de continuer à lever des exceptions dans la pile des appels - de l'attraper là où cela se produit et d'y faire face
Krakkos
2
mais pourquoi même les jeter si vous pouvez le manipuler directement. si le mot de passe est erroné ou quelque chose ne va pas, je laisse simplement un if retourner un faux et donner une erreur
My1
" fichier manquant sur le disque " La plupart des frameworks de langage, par exemple le framework .NET, fournit également des API pour vérifier l'existence des fichiers. Pourquoi ne pas les utiliser avant d'accéder directement au fichier!
user1451111
23

D'autres proposent que les exceptions ne soient pas utilisées car la mauvaise connexion est à prévoir dans un flux normal si l'utilisateur fait une erreur de frappe. Je ne suis pas d'accord et je ne comprends pas le raisonnement. Comparez-le avec l'ouverture d'un fichier .. si le fichier n'existe pas ou n'est pas disponible pour une raison quelconque, une exception sera levée par le framework. Utiliser la logique ci-dessus était une erreur de Microsoft. Ils auraient dû renvoyer un code d'erreur. Idem pour l'analyse, les webrequests, etc., etc.

Je ne considère pas qu'une mauvaise connexion fasse partie d'un flux normal, c'est exceptionnel. Normalement, l'utilisateur tape le mot de passe correct et le fichier existe. Les cas exceptionnels sont exceptionnels et il est parfaitement possible d'utiliser des exceptions pour ceux-ci. Compliquer votre code en propageant les valeurs de retour à travers n niveaux vers le haut de la pile est une perte d'énergie et entraînera un code désordonné. Faites la chose la plus simple qui pourrait fonctionner. N'optimisez pas prématurément en utilisant des codes d'erreur, des choses exceptionnelles par définition se produisent rarement et les exceptions ne coûtent rien sauf si vous les jetez.

Bjorn Reppen
la source
Sauf que vous pouvez vérifier que le fichier existe avant d'appeler open (selon votre framework bien sûr). Donc, cette fonctionnalité existe et donc si le fichier disparaît entre la vérification et que vous essayez de l'ouvrir, c'est l'exception.
Blowdart
7
Le fichier existant ne signifie pas que l'utilisateur est autorisé à écrire dans le fichier par exemple. La vérification de chaque problème possible est vraiment fastidieuse et sujette aux erreurs. + vous dupliquez le code (SEC).
Bjorn Reppen
Un point avec l'exception de mot de passe invalide est que toute lenteur par rapport à la solution de code retour ne sera pas perceptible pour l'utilisateur humain tapant un mot de passe.
paperhorse
7
"La complication de votre code en propageant les valeurs de retour à travers n niveaux vers le haut de la pile est une perte d'énergie et entraînera un code désordonné". C'est, pour moi, une très bonne raison d'utiliser des exceptions. Un bon code comprend généralement de petites fonctions. Vous ne voulez pas transmettre ce code d'erreur encore et encore d'une petite fonction à une autre.
beluchin
Je pense que la confusion résulte d'une hypothèse peut-être qu'un résultat prévisible d'une loginméthode de type serait qu'un mot de passe peut être incorrect, qu'il peut en fait être utilisé pour le déterminer, et ne voudrait pas avoir d'exception dans ce cas; alors que dans un file openscénario de type, qui a un résultat particulier souhaité - si le système n'est pas en mesure de fournir le résultat en raison de paramètres d'entrée incorrects ou d'un facteur externe, c'est une utilisation parfaitement logique d'une exception.
theMayer
17

Les exceptions sont un effet quelque peu coûteux, si par exemple un utilisateur fournit un mot de passe non valide, il est généralement préférable de renvoyer un indicateur d'échec ou un autre indicateur qu'il n'est pas valide.

Cela est dû à la façon dont les exceptions sont gérées, les vraies entrées incorrectes et les éléments d'arrêt critiques uniques doivent être des exceptions, mais pas des informations de connexion ayant échoué.

Vendeurs Mitchel
la source
14

Je pense que vous ne devriez lever une exception que lorsque vous ne pouvez rien faire pour sortir de votre état actuel. Par exemple, si vous allouez de la mémoire et qu'il n'y en a pas à allouer. Dans les cas que vous mentionnez, vous pouvez clairement récupérer de ces états et renvoyer un code d'erreur à votre appelant en conséquence.


Vous verrez de nombreux conseils, y compris dans les réponses à cette question, que vous ne devez lever des exceptions que dans des circonstances "exceptionnelles". Cela semble superficiellement raisonnable, mais c'est un conseil erroné, car il remplace une question ("quand dois-je lever une exception") par une autre question subjective ("ce qui est exceptionnel"). Suivez plutôt les conseils de Herb Sutter (pour C ++, disponible dans l' article Dr Dobbs Quand et comment utiliser les exceptions , et aussi dans son livre avec Andrei Alexandrescu, C ++ Coding Standards ): lève une exception si, et seulement si

  • une condition préalable n'est pas remplie (ce qui rend généralement impossible l'une des situations suivantes) ou
  • l'alternative ne remplirait pas une condition ou
  • l'alternative ne parviendrait pas à maintenir un invariant.

Pourquoi est-ce mieux? Ne remplace-t-il pas la question par plusieurs questions sur les conditions préalables, les postconditions et les invariants? C'est mieux pour plusieurs raisons liées.

  • Les conditions préalables, les postconditions et les invariants sont des caractéristiques de conception de notre programme (son API interne), tandis que la décision throwest un détail d'implémentation. Cela nous oblige à garder à l'esprit que nous devons considérer la conception et sa mise en œuvre séparément, et notre travail lors de la mise en œuvre d'une méthode consiste à produire quelque chose qui satisfait les contraintes de conception.
  • Il nous oblige à penser en termes de conditions préalables, postconditions et invariants, qui sont les seules hypothèses que les appelants de notre méthode devraient faire, et sont exprimées avec précision, permettant un couplage lâche entre les composants de notre programme.
  • Ce couplage lâche nous permet alors de refactoriser la mise en œuvre, si nécessaire.
  • Les post-conditions et les invariants sont testables; il en résulte un code qui peut être facilement testé unitaire, car les post-conditions sont des prédicats que notre code de test unitaire peut vérifier (affirmer).
  • Penser en termes de post-conditions produit naturellement un design qui a du succès en tant que post-condition , qui est le style naturel pour utiliser des exceptions. Le chemin d'exécution normal ("heureux") de votre programme est présenté de façon linéaire, avec tout le code de gestion des erreurs déplacé vers les catchclauses.
Raedwald
la source
10

Je dirais qu'il n'y a pas de règles strictes et rapides sur le moment d'utiliser les exceptions. Cependant, il y a de bonnes raisons de les utiliser ou de ne pas les utiliser:

Raisons d'utiliser des exceptions:

  • Le flux de code pour le cas commun est plus clair
  • Peut renvoyer des informations d'erreur complexes en tant qu'objet (bien que cela puisse également être réalisé en utilisant le paramètre d'erreur "out" passé par référence)
  • Les langages fournissent généralement une certaine facilité pour gérer le nettoyage soigné en cas d'exception (essayez / enfin en Java, en utilisant en C #, RAII en C ++)
  • Dans le cas où aucune exception n'est levée, l'exécution peut parfois être plus rapide que la vérification des codes retour
  • En Java, les exceptions vérifiées doivent être déclarées ou interceptées (bien que cela puisse être une raison contre)

Raisons de ne pas utiliser d'exceptions:

  • Parfois, c'est exagéré si la gestion des erreurs est simple
  • Si les exceptions ne sont pas documentées ou déclarées, elles peuvent être décelées en appelant du code, ce qui peut être pire que si le code appelant a simplement ignoré un code de retour (sortie d'application vs échec silencieux - ce qui est pire peut dépendre du scénario)
  • En C ++, le code qui utilise des exceptions doit être sûr d'exception (même si vous ne les lancez pas ou ne les interceptez pas, mais appelez une fonction de lancement indirectement)
  • En C ++, il est difficile de dire quand une fonction peut lancer, donc vous devez être paranoïaque à propos de la sécurité des exceptions si vous les utilisez
  • Lancer et intercepter des exceptions est généralement beaucoup plus cher que de vérifier un indicateur de retour

En général, je serais plus enclin à utiliser des exceptions en Java qu'en C ++ ou C #, car je suis d'avis qu'une exception, déclarée ou non, fait fondamentalement partie de l'interface formelle d'une fonction, car changer votre garantie d'exception peut casser le code d'appel. Le plus grand avantage de les utiliser dans Java IMO, c'est que vous savez que votre appelant DOIT gérer l'exception, ce qui améliore les chances de comportement correct.

Pour cette raison, dans n'importe quel langage, je dériverais toujours toutes les exceptions dans une couche de code ou d'API à partir d'une classe commune, de sorte que le code appelant puisse toujours garantir d'attraper toutes les exceptions. Je considérerais également qu'il est mauvais de lancer des classes d'exception spécifiques à l'implémentation lors de l'écriture d'une API ou d'une bibliothèque (c'est-à-dire envelopper les exceptions des couches inférieures afin que l'exception que votre appelant reçoive soit compréhensible dans le contexte de votre interface).

Notez que Java fait la distinction entre les exceptions générales et les exceptions d'exécution dans la mesure où ces dernières n'ont pas besoin d'être déclarées. Je n'utiliserais les classes d'exception Runtime que si vous savez que l'erreur est le résultat d'un bogue dans le programme.

Robert
la source
5

Les classes d'exception sont comme les classes "normales". Vous créez une nouvelle classe quand il "est" un type d'objet différent, avec différents champs et différentes opérations.

En règle générale, vous devez essayer d'équilibrer le nombre d'exceptions et la granularité des exceptions. Si votre méthode lève plus de 4 à 5 exceptions différentes, vous pouvez probablement en fusionner certaines dans des exceptions plus "générales", (par exemple dans votre cas "AuthenticationFailedException"), et utiliser le message d'exception pour détailler ce qui s'est passé. À moins que votre code ne gère chacun d'eux différemment, vous n'avez pas besoin de créer de nombreuses classes d'exception. Et si c'est le cas, vous pouvez simplement renvoyer une énumération avec l'erreur qui s'est produite. C'est un peu plus propre de cette façon.

Shachar
la source
5

Si son code s'exécute dans une boucle qui provoquera probablement une exception encore et encore, alors lancer des exceptions n'est pas une bonne chose, car elles sont assez lentes pour les grands N. Mais il n'y a rien de mal à lancer des exceptions personnalisées si les performances ne sont pas un problème. Assurez-vous simplement que vous avez une exception de base qu'ils héritent tous, appelée BaseException ou quelque chose comme ça. BaseException hérite de System.Exception, mais toutes vos exceptions héritent de BaseException. Vous pouvez même avoir une arborescence de types d'exception pour regrouper des types similaires, mais cela peut ou non être exagéré.

Donc, la réponse courte est que si cela n'entraîne pas une baisse significative des performances (ce qui ne devrait pas être le cas sauf si vous lancez de nombreuses exceptions), alors allez-y.

Charles Graham
la source
J'ai vraiment aimé votre commentaire sur les exceptions à l'intérieur d'une boucle et j'ai pensé essayer moi-même. J'ai écrit un exemple de programme exécutant une boucle int.MaxValueet générant une exception de «division par zéro». La version IF / ELSE, dans laquelle je vérifiais si le dividende n'est pas nul avant l'opération de division , terminée en 6082 ms et 15407722 ticks tandis que la version TRY / CATCH, dans laquelle je générais l'exception et interceptais l'exception , terminée en 28174385 ms et 71371326155 ticks: un énorme 4632 fois plus que la version if / else.
user1451111
3

Je suis d'accord avec japollock là-haut - jetez une acceptation lorsque vous n'êtes pas sûr du résultat d'une opération. Appels aux API, accès aux systèmes de fichiers, appels aux bases de données, etc. À chaque fois que vous dépassez les «limites» de vos langages de programmation.

Je voudrais ajouter, n'hésitez pas à lever une exception standard. Sauf si vous allez faire quelque chose de "différent" (ignorer, envoyer un e-mail, vous connecter, montrer cette image de baleine twitter, etc.), alors ne vous embêtez pas avec des exceptions personnalisées.

dclaysmith
la source
3

la règle de base pour lever des exceptions est assez simple. vous le faites lorsque votre code est entré dans un état INVALIDE NON RÉCUPÉRABLE. si les données sont compromises ou si vous ne pouvez pas reprendre le traitement qui a eu lieu jusqu'à ce point, vous devez y mettre fin. en effet, que pouvez-vous faire d'autre? votre logique de traitement finira par échouer ailleurs. si vous pouvez récupérer d'une manière ou d'une autre, faites-le et ne lancez pas d'exception.

dans votre cas particulier, si vous avez été forcé de faire quelque chose de stupide comme accepter un retrait d'argent et ensuite seulement vérifier l'utilisateur / mot de passe, vous devez mettre fin au processus en lançant une exception pour informer que quelque chose de mauvais s'est produit et éviter d'autres dommages.

Goran
la source
2

En général, vous souhaitez lever une exception pour tout ce qui peut arriver dans votre application qui est "Exceptionnel"

Dans votre exemple, ces deux exceptions semblent vous les appeler via une validation de mot de passe / nom d'utilisateur. Dans ce cas, on peut affirmer qu'il n'est pas vraiment exceptionnel que quelqu'un tape un nom d'utilisateur / mot de passe.

Ce sont des "exceptions" au flux principal de votre UML mais ce sont plutôt des "branches" dans le traitement.

Si vous tentiez d'accéder à votre fichier ou base de données passwd et n'y parveniez pas, ce serait un cas exceptionnel et justifierait de lever une exception.

Gord
la source
« Si vous tentiez d'accéder à votre fichier ou base de données passwd et que vous n'y arriviez pas, ce serait un cas exceptionnel et justifierait de lever une exception . Pourquoi ne pas les utiliser avant d'accéder directement au fichier!
user1451111
2

Premièrement, si les utilisateurs de votre API ne sont pas intéressés par des échecs spécifiques et précis, alors leur avoir des exceptions spécifiques n'a aucune valeur.

Comme il n'est souvent pas possible de savoir ce qui peut être utile à vos utilisateurs, une meilleure approche consiste à avoir les exceptions spécifiques, mais assurez-vous qu'elles héritent d'une classe commune (par exemple, std :: exception ou ses dérivés en C ++). Cela permet à votre client de détecter des exceptions spécifiques s'il le souhaite, ou l'exception plus générale s'il ne s'en soucie pas.

Jason Etheridge
la source
2

Les exceptions sont destinées aux événements qui sont des comportements anormaux, des erreurs, des échecs, etc. Le comportement fonctionnel, l'erreur de l'utilisateur, etc., devrait être géré par la logique du programme à la place. Puisqu'un mauvais compte ou mot de passe est une partie attendue du flux logique dans une routine de connexion, il devrait être capable de gérer ces situations sans exception.

Joe Skora
la source
2

J'ai des problèmes philosophiques avec l'utilisation des exceptions. Fondamentalement, vous vous attendez à ce qu'un scénario spécifique se produise, mais plutôt que de le traiter explicitement, vous repoussez le problème pour qu'il soit traité "ailleurs". Et où que soit "ailleurs" peut être deviné par n'importe qui.

Dan
la source
2

Je dirais que généralement tout fondamentalisme mène à l'enfer.

Vous ne voudriez certainement pas vous retrouver avec un flux basé sur des exceptions, mais éviter complètement les exceptions est également une mauvaise idée. Vous devez trouver un équilibre entre les deux approches. Ce que je ne ferais pas, c'est de créer un type d'exception pour chaque situation exceptionnelle. Ce n'est pas productif.

Ce que je préfère généralement, c'est de créer deux types d'exceptions de base qui sont utilisées dans tout le système: LogicalException et TechnicalException . Ceux-ci peuvent être distingués par des sous-types si nécessaire, mais ce n'est généralement pas nécessaire.

L'exception technique dénote l'exception vraiment inattendue comme le serveur de base de données étant en panne, la connexion au service Web a déclenché l'IOException et ainsi de suite.

D'un autre côté, les exceptions logiques sont utilisées pour propager la situation erronée la moins grave aux couches supérieures (généralement un résultat de validation).

Veuillez noter que même l'exception logique n'est pas destinée à être utilisée régulièrement pour contrôler le flux du programme, mais plutôt pour mettre en évidence la situation dans laquelle le flux doit réellement se terminer. Lorsqu'ils sont utilisés en Java, les deux types d'exceptions sont des sous-classes RuntimeException et la gestion des erreurs est fortement orientée vers l'aspect.

Ainsi, dans l'exemple de connexion, il pourrait être judicieux de créer quelque chose comme AuthenticationException et de distinguer les situations concrètes par des valeurs d'énumération telles que UsernameNotExisting , PasswordMismatch, etc. . Vous pouvez également facilement utiliser un mécanisme générique de gestion des exceptions, car vous avez catégorisé les exceptions et savez assez bien quoi propager à l'utilisateur et comment.

Notre utilisation typique consiste à lever l'exception LogicalException lors de l'appel du service Web lorsque l'entrée de l'utilisateur n'était pas valide. L'exception est marshallée dans le détail SOAPFault, puis à nouveau non-marshallée sur l'exception sur le client, ce qui entraîne l'affichage de l'erreur de validation sur un certain champ de saisie de page Web, car l'exception a un mappage approprié vers ce champ.

Ce n'est certainement pas la seule situation: vous n'avez pas besoin d'appuyer sur le service Web pour lever l'exception. Vous êtes libre de le faire dans toute situation exceptionnelle (comme dans le cas où vous devez échouer rapidement) - tout est à votre discrétion.

Petr Macek
la source
2

pour moi, une exception doit être levée lorsqu'une règle technique ou commerciale requise échoue. par exemple, si une entité automobile est associée à un ensemble de 4 pneus ... si un ou plusieurs pneus sont nuls ... une exception doit être déclenchée "NotEnoughTiresException", car elle peut être interceptée à différents niveaux du système et avoir un impact significatif ce qui signifie à travers l'exploitation forestière. d'ailleurs si on essaye juste de contrôler le flux nul et d'éviter l'instanciation de la voiture. nous pourrions ne jamais trouver la source du problème, car le pneu n'est pas censé être nul en premier lieu.

Genjuro
la source
1

la principale raison pour éviter de lever une exception est qu'il y a beaucoup de frais généraux impliqués dans le lancement d'une exception.

Une chose que l'article ci-dessous indique est qu'une exception est pour des conditions et des erreurs exceptionnelles.

Un mauvais nom d'utilisateur n'est pas nécessairement une erreur de programme mais une erreur d'utilisateur ...

Voici un bon point de départ pour les exceptions dans .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

Sam
la source
1

Le lancement d'exceptions entraîne le déroulement de la pile, ce qui a des répercussions sur les performances (il est admis que les environnements gérés modernes se sont améliorés). Lancer et attraper des exceptions à plusieurs reprises dans une situation imbriquée serait une mauvaise idée.

Probablement plus important que cela, les exceptions sont destinées à des conditions exceptionnelles. Ils ne doivent pas être utilisés pour un flux de contrôle ordinaire, car cela nuirait à la lisibilité de votre code.

Arno
la source
1

J'ai trois types de conditions que j'attrape.

  1. Une entrée incorrecte ou manquante ne doit pas faire exception. Utilisez à la fois js côté client et regex côté serveur pour détecter, définir les attributs et renvoyer vers la même page avec les messages.

  2. L'AppException. Il s'agit généralement d'une exception que vous détectez et lancez dans votre code. En d'autres termes, ce sont ceux que vous attendez (le fichier n'existe pas). Enregistrez-le, définissez le message et renvoyez-le à la page d'erreur générale. Cette page contient généralement quelques informations sur ce qui s'est passé.

  3. L'exception inattendue. Ce sont ceux que vous ne connaissez pas. Enregistrez-le avec les détails et transférez-les sur une page d'erreur générale.

J'espère que cela t'aides

Michael
la source
1

La sécurité est liée à votre exemple: vous ne devez pas dire à un attaquant qu'un nom d'utilisateur existe, mais le mot de passe est incorrect. Ce sont des informations supplémentaires que vous n'avez pas besoin de partager. Dites simplement "le nom d'utilisateur ou le mot de passe est incorrect".

anon
la source
1

La réponse simple est, chaque fois qu'une opération est impossible (à cause de l'une ou l'autre application OU parce qu'elle violerait la logique métier). Si une méthode est invoquée et qu'il est impossible de faire ce pour quoi la méthode a été écrite, lancez une exception. Un bon exemple est que les constructeurs lèvent toujours des ArgumentExceptions si une instance ne peut pas être créée à l'aide des paramètres fournis. Un autre exemple est InvalidOperationException, qui est levée lorsqu'une opération ne peut pas être effectuée en raison de l'état d'un autre membre ou de membres de la classe.

Dans votre cas, si une méthode telle que Login (nom d'utilisateur, mot de passe) est invoquée, si le nom d'utilisateur n'est pas valide, il est en effet correct de lever une UserNameNotValidException ou PasswordNotCorrectException si le mot de passe est incorrect. L'utilisateur ne peut pas être connecté en utilisant le (s) paramètre (s) fourni (s) c'est impossible car cela violerait l'authentification), alors lève une exception. Bien que je puisse faire hériter vos deux exceptions de ArgumentException.

Cela dit, si vous ne souhaitez PAS lever une exception car un échec de connexion peut être très courant, une stratégie consiste à créer une méthode qui renvoie des types qui représentent différents échecs. Voici un exemple:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

La plupart des développeurs apprennent à éviter les exceptions en raison des frais généraux causés par leur lancement. C'est génial d'être soucieux des ressources, mais généralement pas au détriment de la conception de votre application. C'est probablement la raison pour laquelle on vous a dit de ne pas lever vos deux exceptions. L'utilisation ou non d'exceptions se résume généralement à la fréquence à laquelle l'exception se produira. Si c'est un résultat assez courant ou assez prévisible, c'est à ce moment-là que la plupart des développeurs éviteront les exceptions et créeront plutôt une autre méthode pour indiquer l'échec, en raison de la consommation supposée de ressources.

Voici un exemple pour éviter d'utiliser des exceptions dans un scénario comme celui qui vient d'être décrit, en utilisant le modèle Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
Chris
la source
1

À mon avis, la question fondamentale devrait être de savoir si l'on peut s'attendre à ce que l'appelant veuille continuer le déroulement normal du programme si une condition se produit. Si vous ne le savez pas, utilisez des méthodes doSomething et trySomething distinctes, où la première renvoie une erreur et la seconde non, ou une routine qui accepte un paramètre pour indiquer si une exception doit être levée en cas d'échec). Envisagez une classe pour envoyer des commandes à un système distant et signaler les réponses. Certaines commandes (par exemple, redémarrage) obligeront le système distant à envoyer une réponse mais ne répondront pas pendant un certain temps. Il est donc utile de pouvoir envoyer une commande "ping" et de savoir si le système distant répond dans un délai raisonnable sans avoir à lever d'exception s'il ne le fait pas ' t (l'appelant s'attendrait probablement à ce que les premières tentatives de "ping" échouent, mais l'une finirait par fonctionner). D'un autre côté, si l'on a une séquence de commandes comme:

  exchange_command ("open tempfile");
  exchange_command ("écrire des données de fichier temporaire {n'importe quoi}");
  exchange_command ("écrire des données de fichier temporaire {n'importe quoi}");
  exchange_command ("écrire des données de fichier temporaire {n'importe quoi}");
  exchange_command ("écrire des données de fichier temporaire {n'importe quoi}");
  exchange_command ("close tempfile");
  exchange_command ("copier le fichier temporaire dans le fichier réel");

on voudrait l'échec d'une opération pour abandonner toute la séquence. Bien que l'on puisse vérifier chaque opération pour s'assurer qu'elle a réussi, il est plus utile que la routine exchange_command () lève une exception si une commande échoue.

En fait, dans le scénario ci-dessus, il peut être utile d'avoir un paramètre pour sélectionner un certain nombre de modes de gestion des échecs: ne jamais lever d'exceptions, lever des exceptions pour les erreurs de communication uniquement, ou lever des exceptions dans tous les cas où une commande ne renvoie pas un "succès" "indication.

supercat
la source
1

"PasswordNotCorrectException" n'est pas un bon exemple d'utilisation d'exceptions. Il faut s'attendre à ce que les utilisateurs se trompent sur leurs mots de passe, ce n'est donc pas une exception à mon humble avis. Vous en récupérez probablement même, affichant un joli message d'erreur, il ne s'agit donc que d'une vérification de validité.

Les exceptions non gérées arrêteront éventuellement l'exécution - ce qui est bien. Si vous retournez des codes faux, nuls ou d'erreur, vous devrez gérer seul l'état du programme. Si vous oubliez de vérifier les conditions quelque part, votre programme peut continuer à fonctionner avec des données erronées et vous aurez peut-être du mal à comprendre ce qui s'est passé et .

Bien sûr, vous pourriez causer le même problème avec les instructions catch vides, mais au moins, les repérer est plus facile et ne nécessite pas de comprendre la logique.

Donc, en règle générale:

Utilisez-les où vous ne voulez pas ou ne pouvez tout simplement pas récupérer d'une erreur.

DanMan
la source
0

Vous pouvez utiliser des exceptions génériques un peu pour ces conditions. Par exemple, ArgumentException est destiné à être utilisé lorsque quelque chose ne va pas avec les paramètres d'une méthode (à l'exception de ArgumentNullException). Généralement, vous n'auriez pas besoin d'exceptions comme LessThanZeroException, NotPrimeNumberException etc. Pensez à l'utilisateur de votre méthode. Le nombre de conditions qu'elle voudra gérer spécifiquement est égal au nombre du type des exceptions que votre méthode doit lever. De cette façon, vous pouvez déterminer le niveau de détail des exceptions dont vous disposez.

Soit dit en passant, essayez toujours de fournir aux utilisateurs de vos bibliothèques des moyens d'éviter les exceptions. TryParse est un bon exemple, il existe pour que vous n'ayez pas à utiliser int.Parse et à attraper une exception. Dans votre cas, vous souhaiterez peut-être fournir certaines méthodes pour vérifier si le nom d'utilisateur est valide ou le mot de passe est correct afin que vos utilisateurs (ou vous) n'aient pas à faire beaucoup de gestion des exceptions. Nous espérons que cela se traduira par un code plus lisible et de meilleures performances.

Serhat Ozgel
la source
0

En fin de compte, la décision se résume à savoir s'il est plus utile de traiter les erreurs au niveau de l'application comme celle-ci en utilisant la gestion des exceptions, ou via votre propre mécanisme personnalisé comme le retour des codes d'état. Je ne pense pas qu'il y ait une règle stricte sur ce qui est mieux, mais je considérerais:

  • Qui appelle votre code? S'agit-il d'une API publique quelconque ou d'une bibliothèque interne?
  • Quelle langue utilisez-vous? Si c'est Java, par exemple, le fait de lancer une exception (vérifiée) impose une charge explicite à votre appelant pour gérer cette condition d'erreur d'une manière ou d'une autre, par opposition à un état de retour qui pourrait être ignoré. Cela pourrait être bon ou mauvais.
  • Comment sont gérées les autres conditions d'erreur dans la même application? Les appelants ne voudront pas traiter avec un module qui gère les erreurs de manière idiosyncratique contrairement à toute autre chose dans le système.
  • Combien de choses peuvent mal tourner avec la routine en question et comment seraient-elles gérées différemment? Considérez la différence entre une série de blocs catch qui gèrent différentes erreurs et un commutateur sur un code d'erreur.
  • Avez-vous des informations structurées sur l'erreur que vous devez renvoyer? Lancer une exception vous donne un meilleur endroit pour mettre ces informations que de simplement renvoyer un statut.
eli
la source
0

Il existe deux principales classes d'exceptions:

1) Exception système (par exemple, connexion à la base de données perdue) ou 2) Exception utilisateur. (par exemple, validation de la saisie utilisateur, «le mot de passe est incorrect»)

J'ai trouvé utile de créer ma propre classe d'exception utilisateur et lorsque je veux lancer une erreur utilisateur, je veux être traité différemment (c'est-à-dire l'erreur ressourcée affichée à l'utilisateur) alors tout ce que je dois faire dans mon gestionnaire d'erreurs principal est de vérifier le type d'objet :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If
Croustillant
la source
0

Quelques éléments utiles à considérer pour décider si une exception est appropriée:

  1. quel niveau de code vous souhaitez exécuter après que le candidat d'exception se soit produit - c'est-à-dire, combien de couches de la pile d'appels devraient se dérouler. Vous souhaitez généralement gérer une exception aussi près que possible de son emplacement. Pour la validation du nom d'utilisateur / mot de passe, vous devriez normalement gérer les échecs dans le même bloc de code, plutôt que de laisser une exception se propager. Une exception n'est donc probablement pas appropriée. (OTOH, après trois tentatives de connexion infructueuses, le flux de contrôle peut se déplacer ailleurs et une exception peut être appropriée ici.)

  2. Cet événement est-il quelque chose que vous voudriez voir dans un journal des erreurs? Toutes les exceptions ne sont pas écrites dans un journal d'erreurs, mais il est utile de se demander si cette entrée dans un journal d'erreurs serait utile - c'est-à-dire que vous tenteriez de faire quelque chose à ce sujet ou serait la poubelle que vous ignorez.

Mike Kantor
la source