Je construis une API, une fonction qui télécharge un fichier. Cette fonction ne retournera rien / annulera si le fichier a été téléchargé correctement et lève une exception en cas de problème.
Pourquoi une exception et pas seulement une fausse? Parce qu'à l'intérieur d'une exception je peux spécifier la raison de l'échec (pas de connexion, nom de fichier manquant, mot de passe erroné, description de fichier manquante, etc.). Je voulais créer une exception personnalisée (avec une énumération pour aider l'utilisateur de l'API à gérer toutes les erreurs).
Est-ce une bonne pratique ou vaut-il mieux retourner un objet (avec un booléen à l'intérieur, un message d'erreur optionnel et l'énumération des erreurs)?
java
api-design
exceptions
functions
architectural-patterns
Accollativo
la source
la source
Result<T, E>
oùT
est le résultat en cas de succès etE
l'erreur en cas d'échec. Lancer une exception (peut être - pas si sûr en Java) peut être coûteux car cela implique de dérouler la pile d'appels, mais la création d'un objet Exception est bon marché. Évidemment, respectez les modèles établis du langage que vous utilisez, mais ne combinez pas les booléens et les exceptions comme celle-ci.fillInStackTrace();
est appelé dans le super constructeur, dans la classeThrowable
Réponses:
Lancer une exception est simplement un moyen supplémentaire de faire en sorte qu'une méthode retourne une valeur. L'appelant peut vérifier une valeur de retour aussi facilement que détecter une exception et vérifier cela. Par conséquent, décider entre
throw
etreturn
nécessite d'autres critères.Le lancement d'exceptions doit souvent être évité s'il met en danger l'efficacité de votre programme (la construction d'un objet d'exception et le déroulement de la pile d'appels représentent beaucoup plus de travail pour l'ordinateur que de simplement y insérer une valeur). Mais si le but de votre méthode est de télécharger un fichier, le goulot d'étranglement sera toujours les E / S du réseau et du système de fichiers, il est donc inutile d'optimiser la méthode de retour.
C'est également une mauvaise idée de lever des exceptions pour ce qui devrait être un simple flux de contrôle (par exemple lorsqu'une recherche réussit en trouvant sa valeur), car cela viole les attentes des utilisateurs d'API. Mais une méthode qui ne remplit pas son objectif est un cas exceptionnel (ou du moins elle devrait l'être), donc je ne vois aucune raison de ne pas lever d'exception. Et si vous faites cela, vous pourriez tout aussi bien en faire une exception personnalisée et plus informative (mais c'est une bonne idée d'en faire une sous-classe d'une exception standard plus générale comme
IOException
).la source
Il n'y a absolument aucune raison de revenir
true
sur le succès si vous ne revenez pasfalse
sur l'échec. À quoi devrait ressembler le code client?Dans ce cas, l' appelant a quand même besoin d'un bloc try-catch, mais il peut alors mieux écrire:
La
true
valeur de retour est donc complètement hors de propos pour l'appelant. Il suffit donc de garder la méthodevoid
.En général, il existe trois façons de concevoir des modes de défaillance.
void
, lever (vérifié) l'exceptionRetour
true
/false
Ceci est utilisé dans certaines API plus anciennes, principalement de style C. Les inconvénients sont évidents, vous n'avez aucune idée de ce qui s'est mal passé. PHP le fait assez souvent, conduisant à du code comme celui-ci:
Dans des contextes multithread, c'est encore pire.
Lancer des exceptions (cochées)
C'est une belle façon de procéder. À certains moments, vous pouvez vous attendre à un échec. Java le fait avec des sockets. L'hypothèse de base est qu'un appel doit réussir, mais tout le monde sait que certaines opérations peuvent échouer. Les connexions de socket en font partie. Ainsi, l'appelant est obligé de gérer l'échec. C'est un design agréable, car il garantit que l'appelant gère réellement l'échec et lui donne un moyen élégant de gérer l'échec.
Renvoyer l'objet de résultat
C'est une autre belle façon de gérer cela. Son souvent utilisé pour l'analyse ou tout simplement les choses qui doivent être validées.
Belle logique propre pour l'appelant également.
Il y a un peu de débat quand utiliser le deuxième cas et quand utiliser le troisième. Certaines personnes croient que les exceptions doivent être exceptionnelles et que vous ne devez pas concevoir avec la possibilité d'exceptions à l'esprit, et utiliserez presque toujours la troisième option. C'est bien. Mais nous avons vérifié les exceptions en Java, donc je ne vois aucune raison de ne pas les utiliser. J'utilise des expetions vérifiées lorsque l'hypothèse de base est que l'appel doit réussir (comme l'utilisation d'une socket), mais l'échec est possible, et j'utilise la troisième option lorsque son très peu clair si l'appel doit réussir (comme la validation des données). Mais il y a des opinions différentes à ce sujet.
Dans votre cas, j'irais avec
void
+Exception
. Vous vous attendez à ce que le téléchargement du fichier réussisse, et quand ce n'est pas le cas, c'est exceptionnel. Mais l'appelant est obligé de gérer ce mode d'échec et vous pouvez renvoyer une exception qui décrit correctement le type d'erreur qui s'est produite.la source
Cela revient vraiment à savoir si une défaillance est exceptionnelle ou attendue .
Si une erreur n'est pas quelque chose que vous attendez généralement, il est raisonnablement probable que l'utilisateur de l'API appellera simplement votre méthode sans aucune gestion d'erreur particulière. Lancer une exception permet de faire bouillonner la pile à un endroit où elle se fait remarquer.
Si, d'autre part, les erreurs sont courantes, vous devez optimiser pour le développeur qui les vérifie, et les
try-catch
clauses sont un peu plus encombrantes qu'uneif/elif
ouswitch
série.Concevez toujours une API dans l'état d'esprit de quelqu'un qui l'utilise.
la source
Ne renvoyez pas un booléen si vous n'avez jamais l'intention de revenir
false
. Faites juste la méthodevoid
et documentez qu'elle lèvera une exception (IOException
convient) en cas d'échec.La raison en est que si vous retournez un booléen, l'utilisateur de votre API peut conclure qu'il peut le faire:
Cela ne fonctionnera pas, bien sûr; c'est-à-dire qu'il y a une incongruité entre le contrat de votre méthode et son comportement. Si vous lancez une exception vérifiée, l'utilisateur de la méthode doit gérer l'échec:
la source
Si votre méthode a une valeur de retour, lever une exception peut surprendre ses utilisateurs. Si je vois une méthode renvoyer un booléen, je suis assez convaincu qu'elle renverra true si elle réussit et false si ce n'est pas le cas, et je structurerai mon code en utilisant une clause if-else. Si votre exception doit être basée sur une énumération de toute façon, vous pouvez également renvoyer une valeur enum à la place, similaire à Windows HResults, et conserver une valeur énumérée lorsque la méthode réussit.
Il est également ennuyeux d'avoir une exception de levée de méthode lorsque ce n'est pas la dernière étape d'une procédure. Devoir écrire un essai-attraper et détourner le flux de contrôle vers le bloc de capture est une bonne recette pour les spaghettis et doit être évité.
Si vous allez de l'avant avec des exceptions, essayez de renvoyer void à la place, les utilisateurs comprendront que cela réussit si aucune exception n'est levée, et aucun n'essaiera d'utiliser une clause if-else pour détourner le flux de contrôle.
la source