Meilleure façon de retourner le drapeau d'état et le message d'une méthode en Java
j'ai un scénario trompeusement simple, et je veux une solution simple, mais ce n'est pas évident ce qui est" le plus correct "ou"le plus Java".
disons que j'ai une petite méthode d'authentification(client client) dans une certaine classe. L'authentification pourrait échouer pour un certain nombre de raisons, et je veux retourner un booléen simple pour le flux de contrôle, mais aussi retourner un message de chaîne de caractères pour l'utilisateur. Ce sont les possibilités auxquelles je peux penser:
- retour a booléen, et passer dans un StringBuilder pour recueillir le message. C'est la plus proche d'un style C moyen de le faire.
- lancer une exception au lieu de retourner false, et inclure le message. Je n'aime pas cela, car l'échec n'est pas exceptionnel.
- crée une nouvelle classe appelée AuthenticationStatus avec le booléen et la corde. Cela semble exagéré pour une petite méthode.
- Stockez le message dans une variable membre. Ce serait introduire une condition de race potentielle, et je n'aime pas que cela implique un État qui n'est pas vraiment là.
D'autres suggestions?
Modifier Raté cette option off
- Retourner la valeur null pour le succès - Est-ce dangereux?
Modifier Solution:
je suis allé pour la solution la plus OO et a créé un petit AuthenticationResult classe. Je ne le ferais pas dans une autre langue, mais J'aime bien Java. J'ai aussi aimé la suggestion de retourner une chaîne [] puisque c'est comme le retour nul mais plus sûr. Un avantage de la classe de résultat est que vous pouvez avoir un message de succès avec plus de détails si nécessaire.
17 réponses
retourner un petit objet avec à la fois le drapeau booléen et la corde à l'intérieur est probablement la façon la plus OO-like de le faire, bien que je convienne que cela semble exagéré pour un cas simple comme celui-ci.
une autre alternative est de toujours retourner une chaîne, et avoir null (ou une chaîne vide - vous choisissez lequel) indique le succès. Tant que les valeurs de retour sont clairement expliquées dans les javadocs, il ne devrait pas y avoir de confusion.
vous pouvez utiliser des exceptions....
try {
AuthenticateMethod();
} catch (AuthenticateError ae) {
// Display ae.getMessage() to user..
System.out.println(ae.getMessage());
//ae.printStackTrace();
}
et ensuite si une erreur se produit dans votre méthode D'authentification, vous envoyez une nouvelle Authentifiererror (étend Exception)
éviter de retourner une" valeur sentinelle", surtout nulle. Vous finirez avec une base de codes où les méthodes ne peuvent pas être comprises par l'appelant sans lire l'implémentation. Dans le cas de null, les appelants peuvent se retrouver avec NullPointerExceptions s'ils oublient (ou ne savent pas) que votre méthode peut retourner null.
la suggestion de tuple de Bas Leijdekkers est une bonne suggestion que j'utilise tout le temps si je veux retourner plus d'une valeur d'une méthode. Celui que nous utilisons est P2<A, B>
de la Fonctionnel Java de la bibliothèque. Ce type de type est une union commune de deux autres types (il contient une valeur de chaque type).
exceptions de lancer pour le flux de contrôle est un peu d'une odeur de code, mais les exceptions vérifiées sont une façon d'obtenir plus d'un type de valeur d'une méthode. Il existe cependant d'autres possibilités plus propres.
-
vous pouvez avoir un
Option<T>
classe abstraite avec deux sous-classesSome<T>
etNone<T>
. C'est un peu comme une alternative de type-safe à null, et une bonne façon d'implémenter des fonctions partielles (fonctions dont la valeur de retour n'est pas définie pour certains arguments). La bibliothèque "Java fonctionnel a une classe complèteOption
qui implémenteIterable<T>
, donc vous pouvez faire quelque chose comme ceci:public Option<String> authenticate(String arg) { if (success(arg)) return Option.some("Just an example"); else return Option.none(); } ... for(String s : authenticate(secret)) { privilegedMethod(); }
-
alternativement, vous pouvez utiliser une union disjointe de deux types, comme une
Either<L, R>
classe. Il contient une valeur qui est soit de typeL
ouR
. Cette classe implémenteIterable<T>
pour les deuxL
etR
, donc vous pouvez faire quelque chose comme ceci:public Either<Fail, String> authenticate(String arg) { if (success(arg)) return Either.right("Just an example"); else return Either.left(Fail.authenticationFailure()); } ... Either<Fail, String> auth = authenticate(secret); for(String s : auth.rightProjection()) { privilegedMethod(); } for(Fail f : auth.leftProjection()) { System.out.println("FAIL"); }
Toutes ces classes, P2
, Option
, et Either
sont utiles dans une grande variété de situation.
quelques autres options:
- retourner une valeur enum distincte pour chaque type de défaillance. L'objet enum pourrait contenir le message
- retourner un int et avoir une méthode séparée qui regarde le message approprié d'un tableau
- crée une classe tuple d'utilité générique qui peut contenir deux valeurs. Une telle classe peut être utile dans beaucoup d'autres endroits.
exemple de tuple simple,
class Tuple<L, R> {
public final L left;
public final R right;
public Tuple( L left, R right) {
this.left = left;
this.right = right;
}
}
vous pourriez retourner un ensemble de messages d'erreur, vides indiquant qu'il n'y avait pas de problèmes. C'est une amélioration de votre troisième proposition.
je pense personnellement que la création d'une nouvelle classe appelée AuthenticationStatus avec le booléen et la chaîne est la façon la plus Java comme. Et bien que cela semble exagéré (ce qui pourrait bien être le cas), cela me semble plus propre et plus facile à comprendre.
ce n'est pas parce qu'une authentification défaillante est banale que cela ne veut pas dire qu'elle n'est pas exceptionnelle.
à mon avis, les échecs d'authentification sont le poster-enfant cas d'utilisation pour les exceptions vérifiées. (Bien... peut-être que la non-existence du fichier est le cas d'utilisation canonique, mais l'échec de l'authentification est une fermeture #2.)
j'utilise la "classe minuscule" moi-même, généralement avec une classe intérieure. Je n'aime pas utiliser des arguments pour recueillir des messages.
aussi, si la méthode qui pourrait échouer est" de bas niveau " - comme venant d'un serveur d'application ou de la couche de base de données, je préférerais retourner un Enum avec le statut de retour, et puis traduire cela dans une chaîne au niveau de L'interface graphique. Ne passez pas les chaînes d'utilisateurs au bas de l'échelle si vous allez un jour internationaliser votre code, car alors votre serveur d'applications peut seulement répondre dans une langue à la fois, plutôt que d'avoir des clients différents travaillant dans des langues différentes.
Est-ce la seule méthode où vous avez une telle exigence? Si ce n'est pas le cas, il suffit de générer une classe de réponse générale avec un drapeau issuccessiful et une chaîne de messages, et de l'utiliser partout.
ou vous pourriez juste avoir la méthode de retour null pour montrer le succès (pas jolie, et ne permet pas de retourner un succès et un message).
je choisirais probablement quelque chose comme :
class SomeClass {
public int authenticate (Client client) {
//returns 0 if success otherwise one value per possible failure
}
public String getAuthenticationResultMessage (int authenticateResult) {}
//returns message associated to authenticateResult
}
Avec ce "design", vous pouvez demander un message uniquement lorsque l'authentification échoue (ce qui, je l'espère, est le scénario qui se produit 99,99% du temps ;))
il peut également être de bonne pratique de déléguer la résolution des messages à une autre classe. Mais cela dépend de vos besoins d'application (la plupart du temps, a-t-il besoin de i18n ?)
cela semble être un idiome courant dans d'autres langages de programmation, mais je ne peux pas comprendre lequel ( C je suppose que je lis dans la question ) .
presque la même question est affichée ici et ici
tenter de retourner deux valeurs à partir d'une seule fonction, peut être trompeur. Mais comme il a été prouvé par les tentatives de le faire, il peut être très utile aussi.
Certainement créer et petite classe avec les résultats devrait être la bonne façon de procéder si c'est un flux commun dans l'application comme posté avant.
Voici une citation sur le retour de deux valeurs d'une fonction:
Comme une question de style de programmation, cette idée n'est pas attrayants dans un langage de programmation orienté objet. Retourner des objets pour représenter des résultats de calcul est le l'idiome pour le retour de plusieurs valeur. Quelque suggérer que vous ne devriez pas avoir à déclarer des classes pour des valeurs indépendantes, mais ni l'un ni l'autre ne devrait être indépendant les valeurs sont retournées à partir d'une seule méthode.
j'ai trouvé dans une requête de fonctionnalité pour java d'autoriser multiple return values
regardez la section "évaluation" datée du: 2005-05-06 09:40:08
L'authentification réussie doit être le cas" normal", donc un échec d'authentification est le cas exceptionnel.
Quelles sont les différentes chaînes d'état pour l'utilisateur de toute façon. Je peux voir seulement deux, le succès ou l'échec. Toute information supplémentaire est un problème de sécurité potentiel. Un autre avantage de la solution avec des exceptions est qu'elle ne peut pas être appelée de la mauvaise manière et que le cas d'échec est plus évident. Sans exception, vous écrivez:
if (authenticate()) {
// normal behaviour...
}
else {
// error case...
}
Vous pouvez accidentellement appeler la méthode en ignorant la valeur de retour. Le code" comportement normal "est alors exécuté sans authentification réussie:
authenticate();
// normal behaviour...
si vous utilisez des exceptions, cela ne peut pas se produire. Si vous décidez de ne pas utiliser les exceptions, au moins nommez la méthode pour qu'il soit clair qu'elle retourne un État, E. G.:
if (isAuthenticated()) {
//...
}
il y a beaucoup de bonnes réponses ici donc je vais faire court.
je pense que le défaut d'authentification d'un utilisateur peut être considéré comme un cas valide pour une exception cochée. Si votre style de programmation favorisait les exceptions, il n'y aurait aucune raison de ne pas le faire. Il supprime également le "Comment retourner des valeurs multiples d'une méthode, ma méthode fait une chose il authentifie un utilisateur"
Si vous allez retourner plusieurs valeurs Passez 10 minutes à créer un PairTuple générique (peut aussi être plus qu'un TripleTuple de paire, Je ne vais pas répéter l'exemple ci-dessus) et retourner vos valeurs de cette façon. Je déteste avoir de petits objets de style dto pour retourner diverses valeurs multiples ils encombrent juste l'endroit.
Que Diriez-vous de retourner une corde? Vide ou Null pour le succès. Message d'erreur en cas d'échec. La plus simple qui fonctionne. Cependant pas sûr si elle lit bien.
retournez l'objet. Il vous permet de mettre des fonctionnalités supplémentaires dans la Classe, si vous en avez besoin. Les objets éphémères en Java sont rapides à créer et à collecter.
je choisirais l'option D'Exception en premier lieu.
mais, en deuxième place, je préférerais la technique du style C:
public boolean authenticate(Client client, final StringBuilder sb) {
if (sb == null)
throw new IllegalArgumentException();
if (isOK()) {
sb.append("info message");
return true;
} else {
sb.append("error message");
return false;
}
}
Ce n'est pas si étrange et c'est fait dans de nombreux endroits dans le cadre.
au lieu de créer un objet spécial pour le type de retour, je renvoie habituellement juste un tableau où toutes les informations retournées sont stockées. L'avantage est que vous pouvez étendre ce tableau avec de nouveaux éléments sans créer de nouveaux types et le désordre. L'inconvénient, Vous devez savoir exactement ce que les éléments devraient présenter lorsque le tableau est retourné de la méthode particulière pour l'analyser correctement. Habituellement je suis d'accord sur une certaine structure, comme premier élément est toujours indication booléenne succès, deuxième est la chaîne avec description, le reste est facultatif. Exemple:
public static void main(String[] args)
{
Object[] result = methodReturningStatus();
if(!(Boolean)result[0])
System.out.println("Method return: "+ result[1]);
}
static Object[] methodReturningStatus()
{
Object[] result = new Object[2];
result[0] = false;
result[1] = "Error happened";
return result;
}