J'ai une situation où j'essaie de récupérer un objet. Si la recherche échoue, plusieurs solutions de rechange sont en place, chacune pouvant échouer. Le code ressemble donc à:
try {
return repository.getElement(x);
} catch (NotFoundException e) {
try {
return repository.getSimilarElement(x);
} catch (NotFoundException e1) {
try {
return repository.getParentElement(x);
} catch (NotFoundException e2) {
//can't recover
throw new IllegalArgumentException(e);
}
}
}
Cela a l'air horriblement laid. Je déteste retourner null, mais est-ce mieux dans cette situation?
Element e = return repository.getElement(x);
if (e == null) {
e = repository.getSimilarElement(x);
}
if (e == null) {
e = repository.getParentElement(x);
}
if (e == null) {
throw new IllegalArgumentException();
}
return e;
Existe-t-il d'autres alternatives?
L'utilisation de blocs try-catch imbriqués est-elle un anti-modèle? est liée, mais les réponses sont du type "parfois, mais c'est généralement évitable", sans dire quand ni comment l'éviter.
java
exception-handling
Alex Wittig
la source
la source
NotFoundException
quelque chose d'exceptionnel?Réponses:
La manière habituelle d'éliminer l'imbrication est d'utiliser des fonctions:
Si ces règles de secours sont universelles, vous pouvez envisager de l'implémenter directement dans l'
repository
objet, où vous pourrez peut-être simplement utiliser desif
instructions simples au lieu d'une exception.la source
method
serait un meilleur mot quefunction
.Ce serait vraiment facile avec quelque chose comme une monade Option. Malheureusement, Java n'en a pas. Dans Scala, j'utiliserais le
Try
type pour trouver la première solution réussie.Dans mon état d'esprit de programmation fonctionnelle, je mettais en place une liste de rappels représentant les différentes sources possibles, et les parcourais jusqu'à ce que nous trouvions la première réussie:
Cela ne peut être recommandé que si vous disposez réellement d'un grand nombre de sources ou si vous devez configurer les sources au moment de l'exécution. Sinon, c'est une abstraction inutile et vous profiteriez davantage de garder votre code simple et stupide, et d'utiliser simplement ces vilains try-catch imbriqués.
la source
Try
type dans Scala, pour mentionner les monades et pour la solution utilisant une boucle.Optional
monade ( preuve ) était déjà sorti.Si vous prévoyez que beaucoup de ces appels de référentiel vont être lancés
NotFoundException
, vous pouvez utiliser un wrapper autour du référentiel pour rationaliser le code. Je ne recommanderais pas cela pour des opérations normales, remarquez:la source
À la suggestion de @ amon, voici une réponse plus monadique. C'est une version très réduite, où vous devez accepter quelques hypothèses:
la fonction "unit" ou "return" est le constructeur de classe
l'opération "bind" se produit au moment de la compilation, elle est donc cachée à l'invocation
les fonctions "action" sont également liées à la classe au moment de la compilation
bien que la classe soit générique et englobe toute classe arbitraire E, je pense que c'est en fait exagéré dans ce cas. Mais je l'ai laissé de cette façon comme exemple de ce que vous pourriez faire.
Avec ces considérations, la monade se traduit par une classe wrapper fluide (bien que vous abandonnez beaucoup de flexibilité que vous obtiendriez dans un langage purement fonctionnel):
(cela ne se compilera pas ... certains détails restent inachevés pour garder l'échantillon petit)
Et l'invocation ressemblerait à ceci:
Notez que vous avez la possibilité de composer les opérations de "récupération" comme vous le souhaitez. Il s'arrêtera lorsqu'il obtiendra une réponse ou une exception autre que non trouvée.
J'ai fait ça très vite; ce n'est pas tout à fait vrai, mais j'espère que cela transmet l'idée
la source
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- à mon avis: code diabolique (au sens donné par Jon Skeet)return this
pour créer des appels d'objets de chaînage existe depuis longtemps. Puisque OO implique des objets mutables,return this
est plus ou moins équivalent àreturn null
sans chaînage. Cependant,return new Thing<E>
ouvre la porte à une autre capacité que cet exemple n'entre pas, il est donc important pour ce modèle si vous choisissez de suivre cette voie.CustomerBuilder.withName("Steve").withID(403)
et ce code, car rien.fetchElement().fetchParentElement().fetchSimilarElement()
qu'en voyant ce n'est pas clair ce qui se passe, et c'est la clé ici. Sont-ils tous récupérés? Ce n'est pas cumulable dans ce cas, et donc pas si intuitif. J'ai besoin de voir çaif (answer != null) return this
avant de vraiment comprendre. C'est peut-être juste une question de nom (orFetchParent
), mais c'est quand même "magique".answer
ingetAnswer
et de réinitialiser (effacer) leanswer
champ lui-même avant de renvoyer sa valeur. Sinon, cela rompt en quelque sorte le principe de la séparation commande / requête, car demander à récupérer un élément (interrogation) modifie l'état de votre objet de référentiel (answer
n'est jamais réinitialisé) et affecte le comportementfetchElement
lors de votre prochain appel. Oui, je suis un peu tatillonne, je pense que la réponse est valable, ce n'est pas moi qui l'ai rejetée.Une autre façon de structurer une série de conditions comme celle-ci est de porter un indicateur, ou bien de tester la valeur null (mieux encore, utilisez l'option facultative de Guava pour déterminer quand une bonne réponse est présente) afin de chaîner les conditions ensemble.
De cette façon, vous surveillez l'état de l'élément et effectuez les bons appels en fonction de son état, c'est-à-dire tant que vous n'avez pas encore de réponse.
(Je suis d'accord avec @amon, cependant. Je recommanderais de regarder un modèle Monad, avec un objet wrapper comme
class Repository<E>
celui-ci qui a des membresE answer;
etException error;
. À chaque étape, vérifiez s'il y a une exception, et si oui, ignorez chaque étape restante. à la fin, vous vous retrouvez avec une réponse, l'absence de réponse ou une exception et vous pouvez décider quoi faire avec cela.)la source
Premièrement, il me semble qu'il devrait y avoir une fonction comme
repository.getMostSimilar(x)
(vous devriez choisir un nom plus approprié) car il semble y avoir une logique qui est utilisée pour trouver l'élément le plus proche ou le plus similaire pour un élément donné.Le référentiel peut alors implémenter la logique comme indiqué dans amons post. Cela signifie que le seul cas où une exception doit être levée est quand il n'y a aucun élément unique qui pourrait être trouvé.
Cependant, cela n'est bien sûr possible que si les logiques permettant de trouver l'élément le plus proche peuvent être encapsulées dans le référentiel. Si cela n'est pas possible, veuillez fournir plus d'informations sur la manière (selon quels critères) l'élément le plus proche peut être choisi.
la source