Ich habe eine Situation, in der ich versuche, ein Objekt abzurufen. Wenn die Suche fehlschlägt, sind mehrere Fallbacks vorhanden, von denen jedes fehlschlagen kann. Der Code sieht also so aus:
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);
}
}
}
Das sieht furchtbar hässlich aus. Ich hasse es, null zurückzugeben, aber ist das in dieser Situation besser?
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;
Gibt es noch andere Alternativen?
Ist die Verwendung verschachtelter Try-Catch-Blöcke ein Anti-Pattern? ist verwandt, aber die Antworten lauten "manchmal, aber normalerweise vermeidbar", ohne zu sagen, wann oder wie man es vermeidet.
java
exception-handling
Alex Wittig
quelle
quelle
NotFoundException
eigentlich etwas Außergewöhnliches?Antworten:
Der übliche Weg, das Verschachteln zu eliminieren, besteht in der Verwendung von Funktionen:
Wenn diese Fallback-Regeln universell sind, können Sie sie direkt im
repository
Objekt implementieren , wobei Sie möglicherweiseif
anstelle einer Ausnahme nur einfache Anweisungen verwenden können.quelle
method
wäre ein besseres Wort alsfunction
.Dies wäre mit so etwas wie einer Option Monade wirklich einfach. Leider hat Java diese nicht. In Scala würde ich den
Try
Typ verwenden , um die erste erfolgreiche Lösung zu finden.In meiner Einstellung zur funktionalen Programmierung habe ich eine Liste von Rückrufen erstellt, die die verschiedenen möglichen Quellen darstellen, und diese durchlaufen, bis wir die erste erfolgreiche finden:
Dies kann nur empfohlen werden, wenn Sie wirklich eine große Anzahl von Quellen haben oder wenn Sie die Quellen zur Laufzeit konfigurieren müssen. Andernfalls ist dies eine unnötige Abstraktion, und Sie würden mehr davon profitieren, wenn Sie Ihren Code einfach und dumm halten und nur diese hässlichen, verschachtelten Versuchsfänge verwenden würden.
quelle
Try
Typs in Scala, für die Erwähnung von Monaden und für die Lösung mit einer Schleife.Optional
Monade ( Proof ) bereits freigegeben.Wenn Sie damit rechnen, dass viele dieser Repository-Aufrufe ausgelöst werden
NotFoundException
, können Sie eine Umhüllung des Repositorys verwenden, um den Code zu optimieren. Ich würde dies nicht für den normalen Betrieb empfehlen, wohlgemerkt:quelle
Auf @ amons Vorschlag folgt eine monadischere Antwort. Es ist eine sehr heruntergekommene Version, in der Sie einige Annahmen akzeptieren müssen:
Die Funktion "unit" oder "return" ist der Klassenkonstruktor
Die "Bind" -Operation findet zur Kompilierungszeit statt und ist daher vor dem Aufruf verborgen
Die "Aktions" -Funktionen sind auch zur Kompilierungszeit an die Klasse gebunden
Obwohl die Klasse generisch ist und jede beliebige Klasse E einschließt, denke ich, dass das in diesem Fall tatsächlich übertrieben ist. Aber ich habe es so belassen, als Beispiel dafür, was Sie tun könnten.
Mit diesen Überlegungen wird die Monade in eine fließende Wrapper-Klasse übersetzt (obwohl Sie einen Großteil der Flexibilität aufgeben, die Sie in einer rein funktionalen Sprache erhalten würden):
(dies wird nicht kompiliert ... bestimmte Details bleiben unvollendet, um das Sample klein zu halten)
Und der Aufruf würde so aussehen:
Beachten Sie, dass Sie die Flexibilität haben, die "Abruf" -Operationen nach Ihren Wünschen zusammenzustellen. Es wird angehalten, wenn eine andere Antwort oder Ausnahme als "Nicht gefunden" angezeigt wird.
Ich habe das sehr schnell gemacht; Es ist nicht ganz richtig, aber hoffentlich vermittelt die Idee
quelle
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- Meiner Meinung nach: böser Code (im Sinne von Jon Skeet)return this
gibt ihn schon seit langer Zeit, um verkettete Objektaufrufe zu erstellen. Da es sich bei OO um veränderbare Objekte handelt,return this
ist dies mehr oder weniger gleichbedeutend mitreturn null
ohne Verkettung. Allerdingsreturn new Thing<E>
öffnet die Tür zu einer anderen Fähigkeit , dass dieses Beispiel nicht in sich gehen, so dass es für dieses Muster ist wichtig, wenn Sie diesen Weg gehen wählen.CustomerBuilder.withName("Steve").withID(403)
und diesem Code, da nur aus der Sicht.fetchElement().fetchParentElement().fetchSimilarElement()
nicht klar ist, was passiert, und das ist hier der Schlüssel. Werden sie alle abgeholt? Es ist in diesem Fall nicht akkumulativ und daher auch nicht so intuitiv. Das muss ich erst sehen,if (answer != null) return this
bevor ich es wirklich verstehe. Vielleicht ist es nur eine Frage der richtigen Benennung (orFetchParent
), aber es ist trotzdem "Magie".answer
in zurückzugebengetAnswer
und dasanswer
Feld selbst zurückzusetzen (zu löschen), bevor der Wert zurückgegeben wird. Andernfalls wird das Prinzip der Trennung von Befehlen und Abfragen verletzt, da das Abrufen eines Elements (Abfragen) den Status Ihres Repository-Objekts ändert (answer
niemals zurückgesetzt wird) und das Verhalten beeinflusst,fetchElement
wenn Sie es das nächste Mal aufrufen. Ja, ich nicke ein bisschen, ich denke, die Antwort ist gültig, ich war nicht derjenige, der sie abgelehnt hat.Eine andere Möglichkeit, eine Reihe von Bedingungen wie diese zu strukturieren, besteht darin, ein Flag zu führen oder auf Null zu testen (besser noch: Verwenden Sie Guavas Optional, um festzustellen, wann eine gute Antwort vorliegt), um die Bedingungen miteinander zu verketten.
Auf diese Weise beobachten Sie den Status des Elements und führen die richtigen Aufrufe basierend auf dem Status durch - das heißt, solange Sie noch keine Antwort haben.
(Ich stimme jedoch mit @amon überein. Ich würde empfehlen, ein Monad-Muster mit einem solchen Wrapper-Objekt
class Repository<E>
mit MitgliedernE answer;
und zu betrachtenException error;
. Überprüfen Sie in jeder Phase, ob es eine Ausnahme gibt, und überspringen Sie in diesem Fall jeden verbleibenden Schritt Am Ende haben Sie entweder eine Antwort, keine Antwort oder eine Ausnahme und Sie können entscheiden, was Sie damit tun möchten.)quelle
Zunächst scheint es mir, dass es eine Funktion geben sollte, wie
repository.getMostSimilar(x)
(Sie sollten einen passenderen Namen wählen) geben sollte, da es eine Logik zu geben scheint, die verwendet wird, um das nächste oder ähnlichste Element für ein bestimmtes Element zu finden.Das Repository kann dann die in amons post gezeigte Logik implementieren. Dies bedeutet, dass nur dann eine Ausnahme ausgelöst werden muss, wenn kein einzelnes Element gefunden werden konnte.
Dies ist jedoch natürlich nur möglich, wenn die Logik zum Auffinden des nächstgelegenen Elements in das Repository eingekapselt werden kann. Ist dies nicht möglich, geben Sie bitte an, wie (nach welchen Kriterien) das nächstgelegene Element ausgewählt werden kann.
quelle