Je me retrouve toujours aux prises avec cela ... essayant de trouver le bon équilibre entre try / catching et le code ne devenant pas ce désordre obscène de tabulations, de crochets et d'exceptions renvoyés dans la pile d'appels comme une patate chaude. Par exemple, j'ai une application en cours de développement qui utilise SQLite. J'ai une interface de base de données qui résume les appels SQLite, et un modèle qui accepte les choses à entrer / sortir de la base de données ... Donc, si / quand une exception SQLite se produit, elle doit être lancée vers le modèle (qui l'a appelée ), qui doit le transmettre à celui qui a appelé AddRecord / DeleteRecord / peu importe ...
Je suis un fan d'exceptions plutôt que de renvoyer des codes d'erreur car les codes d'erreur peuvent être ignorés, oubliés, etc., alors qu'une exception doit essentiellement être gérée (d'accord, je pourrais attraper et passer immédiatement ...) certain qu'il doit y avoir un meilleur moyen que ce que je fais en ce moment.
Edit: j'aurais dû formuler cela un peu différemment. Je comprends de relancer comme différents types et tels, je l'ai mal formulé et c'est ma faute. Ma question est ... comment peut-on garder le code propre en le faisant? Cela commence juste à me sembler extrêmement encombré après un certain temps.
la source
Réponses:
Pensez-y en termes de typage fort, même si vous n'utilisez pas un langage fortement typé - si votre méthode ne peut pas retourner le type auquel vous vous attendiez, elle devrait lever une exception.
De plus, plutôt que de lancer l'exception SQLException jusqu'au modèle (ou pire, l'interface utilisateur), chaque couche doit intercepter les exceptions connues et les envelopper / muter / les remplacer par des exceptions adaptées à cette couche:
Cela devrait aider à limiter le nombre d'exceptions que vous recherchez dans chaque couche et vous aider à maintenir un système d'exceptions organisé.
la source
throws SQLException
une méthode qui n'implique pas que SQL soit même impliqué. Et que se passe-t-il si vous décidez que certaines opérations doivent aller dans un magasin de fichiers? Maintenant, vous devez déclarerthrows SQLException, IOException
, etc. Cela va devenir incontrôlable.Les exceptions permettent d'écrire du code plus propre car la majeure partie de celui-ci s'occupe du cas normal, et les cas exceptionnels peuvent être traités plus tard, même dans un contexte différent.
La règle de gestion (capture) des exceptions est que cela doit être fait par un contexte qui peut réellement faire quelque chose. Mais cela a une exception:
Les exceptions doivent être interceptées aux limites des modules (spécialement les limites des couches) et même si ce n'est que pour les enfermer, lever une exception de niveau supérieur qui a un sens pour l'appelant.Chaque module et couche doit masquer ses détails d'implémentation même en ce qui concerne les exceptions (un module de segment de mémoire peut renvoyer HeapFull mais jamais ArrayIndexOutOfBounds).
Dans votre exemple, il est peu probable que les couches supérieures puissent faire quoi que ce soit à propos d'une exception SQLite (si elles le font, alors tout est si couplé à SQLite que vous ne pourrez pas basculer la couche de données vers autre chose). Il y a une poignée de raisons prévisibles pour des choses comme Add / Delete / Update à échouer, et certaines d'entre elles (changements incompatibles dans les transactions simultanées) sont impossibles à récupérer même dans la couche données / persistance (violation des règles d'intégrité, fi). La couche de persistance doit traduire les exceptions en quelque chose de significatif dans les termes de la couche modèle afin que les couches supérieures puissent décider de réessayer ou d'échouer correctement.
la source
En règle générale, vous ne devez intercepter que des exceptions spécifiques (par exemple IOException), et uniquement si vous avez quelque chose de spécifique à faire une fois que vous avez intercepté l'exception.
Sinon, il est souvent préférable de laisser les exceptions remonter à la surface afin qu'elles puissent être exposées et traitées. Certains appellent cela à toute épreuve.
Vous devriez avoir une sorte de gestionnaire à la racine de votre application pour détecter les exceptions non gérées qui se sont propagées par le bas. Cela vous donne la possibilité de présenter, signaler ou gérer l'exception de manière appropriée.
L'encapsulation d'exceptions est utile lorsque vous devez lancer une exception sur un système distribué et que le client n'a pas la définition de l'erreur côté serveur.
la source
Imaginez que vous écrivez une classe de pile. Vous ne placez aucun code de gestion des exceptions dans la classe, car cela pourrait produire les exceptions suivantes.
Une approche simpliste pour encapsuler les exceptions pourrait décider d'encapsuler ces deux exceptions dans une classe d'exception StackError. Cependant, cela manque vraiment le point d'envelopper les exceptions. Si un objet lève une exception de bas niveau, cela devrait signifier que l'objet est cassé. Cependant, il y a un cas où cela est acceptable: lorsque l'objet est en fait cassé.
Le point d'encapsuler les exceptions est que l'objet doit donner des exceptions appropriées pour les erreurs normales. La pile doit soulever StackEmpty et non ArrayIndexError lors de l'éclatement d'une pile vide. L'intention n'est pas d'éviter de lever d'autres exceptions si l'objet ou le code est cassé.
Ce que nous avons vraiment voulons éviter, c'est d'attraper des exceptions de bas niveau qui ont été passées à travers des objets de haut niveau. Une classe de pile qui lève une ArrayIndexError lors de l'éclatement d'une pile vide est un problème mineur. Si vous attrapez réellement cette ArrayIndexError, nous avons un problème sérieux. La propagation des erreurs de bas niveau est un péché beaucoup moins grave que de les attraper.
Pour ramener cela à votre exemple d'une SQLException: pourquoi obtenez-vous des SQLExceptions? L'une des raisons est que vous passez des requêtes non valides. Cependant, si votre couche d'accès aux données génère de mauvaises requêtes, elle est cassée. Il ne doit pas tenter de reconditionner sa rupture dans une exception DataAccessFailure.
Cependant, une exception SQLException peut également survenir en raison d'une perte de connexion à la base de données. Ma stratégie sur ce point consiste à intercepter l'exception à la dernière ligne de défense, à signaler à l'utilisateur que la connectivité à la base de données a été perdue et à l'arrêt. Étant donné que l'application a un accès à perte à la base de données, il n'y a vraiment pas beaucoup plus à faire.
Je ne sais pas à quoi ressemble votre code. Mais il semble que vous traduisiez aveuglément toutes les exceptions en exceptions de niveau supérieur. Vous ne devriez le faire que dans un nombre relativement restreint de cas. La plupart des exceptions de niveau inférieur indiquent des bogues dans votre code. Les attraper et les reconditionner est contre-productif.
la source