Avertissement! Un programmeur C ++ arrive ici avec des idées peut-être différentes sur la façon de gérer les exceptions en essayant de répondre à une question qui concerne certainement un autre langage!
Compte tenu de cette idée:
Par exemple, imaginez que nous avons une ressource d'extraction, qui exécute une requête HTTP et retourne les données récupérées. Et au lieu d'erreurs comme ServiceTemporaryUnavailable ou RateLimitExceeded, nous soulèverions simplement une RetryableError suggérant au consommateur qu'il devrait simplement réessayer la demande et ne pas se soucier d'un échec spécifique.
... une chose que je suggérerais est que vous pourriez mélanger les préoccupations de signaler une erreur avec des plans d'action pour y répondre d'une manière qui pourrait dégrader la généralité de votre code ou nécessiter beaucoup de "points de traduction" pour les exceptions .
Par exemple, si je modélise une transaction impliquant le chargement d'un fichier, elle peut échouer pour plusieurs raisons. Peut-être que le chargement du fichier implique le chargement d'un plugin qui n'existe pas sur la machine de l'utilisateur. Le fichier est peut-être simplement corrompu et nous avons rencontré une erreur lors de son analyse.
Peu importe ce qui se passe, disons que la procédure consiste à signaler ce qui est arrivé à l'utilisateur et à lui demander ce qu'il veut faire ("réessayer, charger un autre fichier, annuler").
Lanceur contre receveur
Cette ligne de conduite s'applique quel que soit le type d'erreur que nous avons rencontré dans ce cas. Il n'est pas intégré à l'idée générale d'une erreur d'analyse, il n'est pas intégré à l'idée générale de l'échec du chargement d'un plugin. Il est intégré à l'idée de rencontrer de telles erreurs lors du contexte précis de chargement d'un fichier (combinaison de chargement d'un fichier et d'échec). Donc, généralement, je le vois, grossièrement parlant, comme la catcher's
responsabilité de déterminer le plan d'action en réponse à une exception levée (ex: inviter l'utilisateur avec des options), pas le thrower's
.
Autrement dit, les sites auxquels les throw
exceptions manquent généralement ce type d'informations contextuelles, surtout si les fonctions qui lancent sont généralement applicables. Même dans un contexte totalement dégénéré quand ils ont ces informations, vous vous retrouvez coincé en termes de comportement de récupération en l'intégrant dans le throw
site. Les sites qui catch
sont généralement ceux qui ont le plus d'informations disponibles pour déterminer une ligne de conduite, et vous donnent un endroit central pour modifier si cette ligne de conduite doit jamais changer pour cette transaction donnée.
Lorsque vous commencez à essayer de lever des exceptions ne signalant plus ce qui ne va pas mais essayant de déterminer quoi faire, cela pourrait dégrader la généralité et la flexibilité de votre code. Une erreur d'analyse ne doit pas toujours conduire à ce type d'invite, elle varie en fonction du contexte dans lequel une telle exception est levée (la transaction dans laquelle elle a été levée).
Le lanceur aveugle
De manière générale, une grande partie de la conception de la gestion des exceptions tourne souvent autour de l'idée d'un lanceur aveugle. Il ne sait pas comment l'exception va être interceptée, ni où. Il en va de même pour les anciennes formes de récupération d'erreurs utilisant la propagation d'erreur manuelle. Les sites qui rencontrent des erreurs n'incluent pas de ligne de conduite utilisateur, ils incorporent uniquement les informations minimales pour signaler le type d'erreur rencontré.
Responsabilités inversées et généralisation du receveur
En y réfléchissant plus attentivement, j'essayais d'imaginer le type de base de code où cela pourrait devenir une tentation. Mon imagination (peut-être erronée) est que votre équipe joue toujours le rôle de "consommateur" ici et implémente également la plupart du code d'appel. Peut-être que vous avez beaucoup de transactions disparates (beaucoup de try
blocs) qui peuvent toutes se heurter aux mêmes ensembles d'erreurs, et toutes devraient, du point de vue de la conception, conduire à une action uniforme de récupération.
En tenant compte des conseils judicieux de la Lightness Races in Orbit's
bonne réponse (qui, je pense, vient vraiment d'un état d'esprit avancé orienté bibliothèque), vous pourriez toujours être tenté de lever des exceptions "que faire", seulement plus près du site de récupération de transaction.
Il pourrait être possible de trouver ici un site intermédiaire et commun de traitement des transactions qui centralise réellement les préoccupations "que faire" mais toujours dans le contexte de la capture.
Cela ne s'appliquerait que si vous pouvez concevoir une sorte de fonction générale que toutes ces transactions externes utilisent (ex: une fonction qui entre une autre fonction à appeler ou une classe de base de transaction abstraite avec un comportement remplaçable modélisant ce site de transaction intermédiaire qui effectue la capture sophistiquée ).
Pourtant, celui-ci pourrait être responsable de la centralisation du plan d'action de l'utilisateur en réponse à une variété d'erreurs possibles, et toujours dans le contexte de la capture plutôt que du lancer. Exemple simple (pseudocode Python-ish, et je ne suis pas du tout un développeur Python expérimenté, donc il pourrait y avoir une façon plus idiomatique de procéder):
def general_catcher(task):
try:
task()
except SomeError1:
# do some uniformly-designed recovery stuff here
except SomeError2:
# do some other uniformly-designed recovery stuff here
...
[Avec un peu de chance avec un meilleur nom que general_catcher
]. Dans cet exemple, vous pouvez transmettre une fonction contenant la tâche à exécuter tout en bénéficiant d'un comportement de capture généralisé / unifié pour tous les types d'exceptions qui vous intéressent, et continuer à étendre ou modifier la partie «que faire» tout vous aimez depuis cet emplacement central et toujours dans un catch
contexte où cela est généralement encouragé. Mieux encore, nous pouvons empêcher les sites de lancer de se préoccuper de "quoi faire" (en préservant la notion de "lanceur aveugle").
Si vous ne trouvez aucune de ces suggestions ici utile et qu'il y a une forte tentation de lever des exceptions "quoi faire" de toute façon, sachez surtout que cela est très anti-idiomatique à tout le moins, ainsi que potentiellement décourageant un état d'esprit généralisé.