Wir verwenden Ausnahmen, damit der Benutzer des Codes auf nützliche Weise mit unerwartetem Verhalten umgehen kann. Normalerweise basieren Ausnahmen auf dem Szenario "Was ist passiert?" FileNotFound
(Die angegebene Datei konnte nicht gefunden werden) oder ZeroDivisionError
(die 1/0
Operation konnte nicht ausgeführt werden).
Was ist, wenn die Möglichkeit besteht, das erwartete Verhalten des Verbrauchers festzulegen?
Stellen Sie sich zum Beispiel vor, wir haben eine fetch
Ressource, die HTTP-Anforderungen ausführt und abgerufene Daten zurückgibt. Und anstelle von Fehlern wie ServiceTemporaryUnavailable
oder RateLimitExceeded
möchten wir RetryableError
dem Verbraucher lediglich vorschlagen, die Anforderung erneut zu versuchen und sich nicht um bestimmte Fehler zu kümmern. Im Grunde schlagen wir dem Anrufer eine Aktion vor - das "was zu tun ist".
Wir tun dies nicht oft, weil wir nicht alle Verwendungszwecke von Verbrauchern kennen. Stellen Sie sich jedoch vor, dass dies eine bestimmte Komponente ist, mit der wir die besten Vorgehensweisen für einen Anrufer kennen. Sollten wir also den Ansatz "Was ist zu tun?" Anwenden?
quelle
if( ! function() ) handle_something();
übergegangen sind - nicht mehr , aber in der Lage, den Fehler an einer Stelle zu behandeln, an der Sie den aufrufenden Kontext tatsächlich kennen In diesem Fall ist der Anrufer ein weiterer Mikrodienst. Lassen Sie die Fangklötze den Fang erledigen.Antworten:
Dies scheitert fast immer bei mindestens einem Ihrer Anrufer, für den dieses Verhalten unglaublich irritierend ist. Gehen Sie nicht davon aus, dass Sie es am besten wissen. Sagen Sie Ihren Benutzern, was passiert, und nicht, was sie tun sollen. In vielen Fällen ist bereits klar, wie eine vernünftige Vorgehensweise aussehen sollte (und falls dies nicht der Fall ist, schlagen Sie dies in Ihrem Benutzerhandbuch vor).
Selbst die in Ihrer Frage angegebenen Ausnahmen belegen Ihre fehlerhafte Annahme: a bedeutet
ServiceTemporaryUnavailable
"Versuchen Sie es später noch einmal" undRateLimitExceeded
"Woah there chill out, passen Sie möglicherweise Ihre Timer-Parameter an und versuchen Sie es in ein paar Minuten noch einmal". Der Benutzer möchte jedoch möglicherweise auch eine Art Alarm auslösenServiceTemporaryUnavailable
(was auf ein Serverproblem hinweist) und nicht fürRateLimitExceeded
(was nicht).Gib ihnen die Wahl .
quelle
RetryableError
.Angesichts dieser Idee:
Ich würde vorschlagen, dass Sie die Bedenken, einen Fehler zu melden, mit Vorgehensweisen verwechseln, um darauf zu reagieren, die die Allgemeingültigkeit Ihres Codes beeinträchtigen oder viele "Übersetzungspunkte" für Ausnahmen erfordern .
Wenn ich beispielsweise eine Transaktion modelliere, bei der eine Datei geladen wird, kann dies aus mehreren Gründen fehlschlagen. Möglicherweise wird beim Laden der Datei ein Plugin geladen, das auf dem Computer des Benutzers nicht vorhanden ist. Möglicherweise ist die Datei einfach beschädigt, und beim Parsen ist ein Fehler aufgetreten.
Egal was passiert, sagen wir, die Vorgehensweise besteht darin, dem Benutzer zu melden, was passiert ist, und ihn aufzufordern, was er dagegen tun möchte ("erneut versuchen, eine andere Datei laden, abbrechen").
Werfer gegen Fänger
Diese Vorgehensweise gilt unabhängig davon, auf welche Art von Fehler wir in diesem Fall gestoßen sind. Es ist nicht in die allgemeine Idee eines Parsing-Fehlers eingebettet. Es ist nicht in die allgemeine Idee eingebettet, dass ein Plugin nicht geladen werden kann. Es ist eingebettet in die Idee, solche Fehler im genauen Kontext des Ladens einer Datei (der Kombination aus Laden einer Datei und Fehlschlagen) zu finden. Typischerweise sehe ich es grob gesagt als die
catcher's
Verantwortung, die Vorgehensweise als Reaktion auf eine ausgelöste Ausnahme (z. B. Aufforderung des Benutzers mit Optionen) zu bestimmen, nicht diethrower's
.Anders ausgedrückt, den Sites mit
throw
Ausnahmen fehlen normalerweise solche Kontextinformationen, insbesondere wenn die auszulösenden Funktionen allgemein anwendbar sind. Selbst in einem völlig entarteten Kontext, in dem diese Informationen vorliegen, können Sie das Wiederherstellungsverhalten verbessern, indem Sie sie in diethrow
Site einbetten . Die Sites,catch
die im Allgemeinen über die meisten verfügbaren Informationen verfügen, um eine Vorgehensweise zu bestimmen, und bieten Ihnen einen zentralen Ort zum Ändern, falls sich diese Vorgehensweise jemals für eine bestimmte Transaktion ändern sollte.Wenn Sie versuchen, Ausnahmen auszulösen, die nicht mehr melden, was falsch ist, sondern ermitteln, was zu tun ist, kann dies die Allgemeingültigkeit und Flexibilität Ihres Codes beeinträchtigen. Ein Parsing-Fehler sollte nicht immer zu einer solchen Aufforderung führen. Er hängt vom Kontext ab, in dem eine solche Ausnahme ausgelöst wird (der Transaktion, unter der sie ausgelöst wurde).
Der blinde Werfer
Nur im Allgemeinen dreht sich ein Großteil des Designs für die Ausnahmebehandlung oft um die Idee eines blinden Werfers. Es weiß nicht, wie und wo die Ausnahme abgefangen wird. Gleiches gilt für noch ältere Formen der Fehlerbehebung mittels manueller Fehlerweitergabe. Websites, bei denen Fehler auftreten, enthalten keine Benutzeraktion. Sie enthalten nur die minimalen Informationen, um zu melden, welche Art von Fehler aufgetreten ist.
Umgekehrte Verantwortlichkeiten und Verallgemeinerung des Fängers
Beim genaueren Überlegen versuchte ich mir die Art von Codebasis vorzustellen, in der dies eine Versuchung werden könnte. Meiner Vorstellung nach (möglicherweise falsch) spielt Ihr Team hier immer noch die Rolle des "Verbrauchers" und implementiert auch den größten Teil des aufrufenden Codes. Möglicherweise gibt es viele unterschiedliche Transaktionen (viele
try
Blöcke), bei denen alle dieselben Fehler auftreten können, und alle sollten aus Entwurfssicht zu einer einheitlichen Vorgehensweise für die Wiederherstellung führen.Unter Berücksichtigung der
Lightness Races in Orbit's
guten Antworten (die meiner Meinung nach von einer fortgeschrittenen bibliotheksorientierten Denkweise herrühren) könnten Sie immer noch versucht sein, Ausnahmen zu machen, die nur näher an der Transaktionswiederherstellungssite liegen.Hier könnte es möglich sein, eine zwischengeschaltete, gemeinsame Transaktionsabwicklungs-Site zu finden, die die "was zu tun ist" -Anliegen tatsächlich zentralisiert, aber immer noch im Kontext des Fangens.
Dies gilt nur, wenn Sie eine Art allgemeine Funktion entwerfen können, die alle diese äußeren Transaktionen verwenden (z. B. eine Funktion, die eine andere aufzurufende Funktion eingibt, oder eine abstrakte Transaktionsbasisklasse mit überschreibbarem Verhalten, die diese zwischengeschaltete Transaktionssite modelliert, die das anspruchsvolle Abfangen ausführt ).
Dieser könnte jedoch dafür verantwortlich sein, die Vorgehensweise des Benutzers als Reaktion auf eine Vielzahl möglicher Fehler zu zentralisieren, und dies immer noch im Kontext des Fangens statt des Werfens. Einfaches Beispiel (Python-isch-Pseudocode, und ich bin nicht im geringsten ein erfahrener Python-Entwickler, daher könnte es eine idiomatischere Vorgehensweise geben):
[Hoffentlich mit einem besseren Namen als
general_catcher
]. In diesem Beispiel können Sie eine Funktion übergeben, die die auszuführende Aufgabe enthält, aber dennoch vom allgemeinen / einheitlichen Fangverhalten für alle Arten von Ausnahmen profitiert, an denen Sie interessiert sind, und den Teil "Was ist zu tun?" Weiterhin erweitern oder ändern Sie möchten von diesem zentralen Ort aus und dennoch in einemcatch
Kontext, in dem dies normalerweise empfohlen wird. Das Beste von allem ist, dass wir die Wurfplätze davon abhalten können, sich mit der Frage zu befassen, was zu tun ist (unter Beibehaltung der Vorstellung des "blinden Werfers").Wenn Sie keinen dieser Vorschläge hier hilfreich finden und die Versuchung groß ist, Ausnahmen zu machen, sollten Sie sich vor allem darüber im Klaren sein, dass dies zumindest sehr antiidiomatisch ist und möglicherweise eine verallgemeinerte Denkweise entmutigt.
quelle
Ich denke, die meiste Zeit wäre es besser, Argumente an die Funktion zu übergeben, um zu sagen, wie mit diesen Situationen umgegangen werden soll.
Betrachten Sie zum Beispiel eine Funktion:
Ich kann RetryPolicy.noRetries () oder RetryPolicy.retries (3) oder was auch immer übergeben. Im Falle eines erneuten Versagens wird die Richtlinie konsultiert, um zu entscheiden, ob es erneut versucht werden soll oder nicht.
quelle
new RetryPolicy().onRateLimitExceeded(STOP).onServiceTemporaryUnavailable(RETRY, 3)
oder so, weil dasRateLimitExceeded
möglicherweise anders gehandhabt werden muss alsServiceTemporaryUnavailable
. Nachdem ich das geschrieben habe, ist mein Gedanke: Wirf lieber eine Ausnahme, weil es flexiblere Kontrolle gibt.