Warum sollte man eine moderne Sprache ohne Ausnahmebehandlungsmechanismus entwerfen?

47

Viele moderne Sprachen bieten umfangreiche Funktionen zur Ausnahmebehandlung , die Programmiersprache Swift von Apple bietet jedoch keinen Mechanismus zur Ausnahmebehandlung .

Ich bin so durchdrungen von Ausnahmen, dass ich Probleme habe, mir darüber Gedanken zu machen, was dies bedeutet. Swift hat Behauptungen und natürlich Rückgabewerte. Aber ich habe Probleme damit, mir vorzustellen, wie meine ausnahmebasierte Denkweise einer Welt ohne Ausnahmen entspricht (und warum eine solche Welt wünschenswert ist ). Gibt es Dinge, die ich in einer Sprache wie Swift nicht machen kann, die ich mit Ausnahmen machen kann? Gewinne ich etwas, indem ich Ausnahmen verliere?

Wie könnte ich zum Beispiel am besten so etwas ausdrücken ?

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
     # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

in einer Sprache (z. B. Swift), in der die Ausnahmebehandlung fehlt?

orome
quelle
11
Sie können Go zur Liste hinzufügen , wenn wir einfach ignorieren, panicwas nicht ganz dasselbe ist. Zusätzlich zu dem, was dort gesagt wird, ist eine Ausnahme nicht viel mehr als eine raffinierte (aber bequeme) Art, eine auszuführen GOTO, obwohl es aus offensichtlichen Gründen niemand so nennt.
JensG
3
Die sortierte Antwort auf Ihre Frage lautet, dass Sie für das Schreiben von Ausnahmen Sprachunterstützung benötigen. Die Sprachunterstützung umfasst im Allgemeinen die Speicherverwaltung. Da eine Ausnahme überall ausgelöst und abgefangen werden kann, muss es eine Möglichkeit geben, Objekte zu entsorgen, die nicht vom Steuerungsfluss abhängen.
Robert Harvey
1
Ich verfolge dich nicht ganz, @Robert. C ++ unterstützt Ausnahmen ohne Garbage Collection.
Karl Bielefeldt
3
@ KarlBielefeldt: Mit großem Aufwand, soweit ich das verstehe. Gibt es andererseits etwas , das in C ++ ohne großen Aufwand durchgeführt wird, zumindest in Bezug auf Aufwand und erforderliche Domänenkenntnisse?
Robert Harvey
2
@ Robert Harvey: Guter Punkt. Ich bin einer von denen, die nicht genug über diese Dinge nachdenken. Ich wurde in den Gedanken hineingelullt, dass ARC GC ist, aber das ist es natürlich nicht. Im Grunde genommen (wenn ich es nur grob fasse), wären Ausnahmen in einer Sprache, in der die Entsorgung von Objekten vom Kontrollfluss abhängt, eine unordentliche Sache (ungeachtet von C ++)?
Orome

Antworten:

33

In der eingebetteten Programmierung waren Ausnahmen traditionell nicht zulässig, da der Aufwand für das Abwickeln des Stapels als inakzeptable Schwankung angesehen wurde, wenn versucht wurde, die Echtzeitleistung aufrechtzuerhalten. Obwohl Smartphones technisch als Echtzeitplattformen betrachtet werden könnten, sind sie jetzt leistungsstark genug, wo die alten Einschränkungen eingebetteter Systeme nicht mehr wirklich gelten. Ich bringe es nur der Gründlichkeit halber zur Sprache.

Ausnahmen werden häufig in funktionalen Programmiersprachen unterstützt, aber so selten verwendet, dass sie möglicherweise auch nicht verwendet werden. Ein Grund ist die verzögerte Auswertung, die gelegentlich auch in Sprachen durchgeführt wird, die nicht standardmäßig verzögert sind. Wenn eine Funktion mit einem anderen Stack ausgeführt wird als der Ort, an dem sie ausgeführt werden soll, ist es schwierig zu bestimmen, wo der Ausnahmebehandler abgelegt werden soll.

Der andere Grund ist, dass erstklassige Funktionen Konstrukte wie Optionen und Futures ermöglichen, mit denen Sie die syntaktischen Vorteile von Ausnahmen flexibler nutzen können. Mit anderen Worten, der Rest der Sprache ist so ausdrucksstark, dass Ausnahmen Ihnen nichts einbringen.

Ich bin nicht mit Swift vertraut, aber das Wenige, was ich über die Fehlerbehandlung gelesen habe, legt nahe, dass die Fehlerbehandlung eher funktionalen Mustern folgt. Ich habe Codebeispiele mit successund failureBlöcke gesehen, die Futures sehr ähnlich sehen.

Hier ist ein Beispiel mit einem Futureaus diesem Scala-Tutorial :

val f: Future[List[String]] = future {
  session.getRecentPosts
}
f onFailure {
  case t => println("An error has occured: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post)
}

Sie können sehen, dass es mit Ausnahmen ungefähr die gleiche Struktur wie Ihr Beispiel hat. Der futureBlock ist wie ein try. Der onFailureBlock ist wie ein Exception-Handler. In Scala wird, wie in den meisten funktionalen Sprachen, Futuredie Sprache selbst vollständig verwendet. Es ist keine spezielle Syntax erforderlich, wie dies bei Ausnahmen der Fall ist. Das heißt, Sie können Ihre eigenen ähnlichen Konstrukte definieren. Fügen Sie beispielsweise einen timeoutBlock oder eine Protokollierungsfunktion hinzu.

Darüber hinaus können Sie die Zukunft weitergeben, von der Funktion zurückgeben, in einer Datenstruktur speichern oder was auch immer. Es ist ein erstklassiger Wert. Sie sind nicht auf Ausnahmen beschränkt, die direkt über den Stapel verteilt werden müssen.

Optionen lösen das Fehlerbehandlungsproblem auf eine etwas andere Art und Weise, die in einigen Anwendungsfällen besser funktioniert. Sie stecken nicht nur mit der einen Methode fest.

Das sind die Dinge, die Sie "gewinnen, indem Sie Ausnahmen verlieren".

Karl Bielefeldt
quelle
1
Dies Futureist im Wesentlichen eine Möglichkeit, den von einer Funktion zurückgegebenen Wert zu untersuchen, ohne anzuhalten, um darauf zu warten. Wie Swift basiert es auf dem Rückgabewert, aber im Gegensatz zu Swift kann die Reaktion auf den Rückgabewert zu einem späteren Zeitpunkt erfolgen (ein bisschen wie bei Ausnahmen). Richtig?
Orome
1
Sie verstehen ein Futurerichtig, aber ich denke, Sie könnten Swift falsch charakterisieren. Siehe zum Beispiel den ersten Teil dieser Stackoverflow-Antwort .
Karl Bielefeldt
Hmm, ich bin neu bei Swift, daher ist es für mich ein bisschen schwierig, diese Antwort zu analysieren. Aber wenn ich mich nicht irre: Das geht im Wesentlichen an einem Handler vorbei, der zu einem späteren Zeitpunkt aufgerufen werden kann . richtig?
Orome
Ja. Sie erstellen im Grunde genommen einen Rückruf, wenn ein Fehler auftritt.
Karl Bielefeldt
Eitherwäre ein besseres Beispiel IMHO
Paweł Prażak
19

Ausnahmen können das Begründen von Code erschweren. Obwohl sie nicht so mächtig sind wie gotos, können sie aufgrund ihrer nicht-lokalen Natur viele der gleichen Probleme verursachen. Nehmen wir zum Beispiel an, Sie haben einen imperativen Code wie diesen:

cleanMug();
brewCoffee();
pourCoffee();
drinkCoffee();

Sie können nicht auf einen Blick erkennen, ob eine dieser Prozeduren eine Ausnahme auslösen kann. Sie müssen die Dokumentation zu jedem dieser Verfahren lesen, um dies herauszufinden. (Einige Sprachen vereinfachen dies geringfügig, indem sie die Typensignatur mit diesen Informationen ergänzen.) Der obige Code wird einwandfrei kompiliert, unabhängig davon, ob eine der Prozeduren ausgelöst wird, wodurch es wirklich einfach wird, die Behandlung einer Ausnahme zu vergessen.

Selbst wenn die Absicht besteht, die Ausnahmebedingung an den Anrufer weiterzugeben, muss häufig zusätzlicher Code hinzugefügt werden, um zu verhindern, dass Dinge in einem inkonsistenten Zustand verbleiben (z. B. wenn Ihre Kaffeemaschine kaputt geht, müssen Sie das Durcheinander bereinigen und zurückkehren die Tasse!). Daher würde Code, der Ausnahmen verwendet, in vielen Fällen genauso komplex aussehen wie Code, der dies aufgrund der zusätzlichen erforderlichen Bereinigung nicht tat.

Ausnahmen können mit einem ausreichend leistungsfähigen Typsystem emuliert werden. Viele der Sprachen, die Ausnahmen vermeiden, verwenden Rückgabewerte, um dasselbe Verhalten zu erzielen. Es ist ähnlich wie in C, aber moderne Typsysteme machen es normalerweise eleganter und auch schwieriger, die Fehlerbedingung zu vergessen. Sie können auch syntaktischen Zucker bereitstellen, um die Dinge weniger umständlich zu machen, manchmal fast so sauber, wie es mit Ausnahmen wäre.

Insbesondere durch die Einbettung der Fehlerbehandlung in das Typsystem anstelle der Implementierung als separates Feature können "Ausnahmen" für andere Dinge verwendet werden, die nicht einmal mit Fehlern zusammenhängen. (Es ist bekannt, dass die Ausnahmebehandlung eigentlich eine Eigenschaft von Monaden ist.)

Rufflewind
quelle
Ist es richtig, welche Art von Typensystem Swift hat, einschließlich der optionalen, welche Art von "leistungsfähigem Typensystem", das dies erreicht?
Orome
2
Ja, dies können optionale und ganz allgemein Summentypen (in Swift / Rust als "enum" bezeichnet) erreichen. Es erfordert jedoch einige zusätzliche Arbeit, um sie benutzerfreundlich zu gestalten: In Swift wird dies mit der optionalen Verkettungssyntax erreicht; in Haskell wird dies mit monadischer Notation erreicht.
Rufflewind
Kann ein "ausreichend leistungsfähiges Typsystem" einen Stack-Trace liefern, wenn es nicht ziemlich nutzlos ist
?
1
+1 für den Hinweis, dass Ausnahmen den Kontrollfluss verdecken. Nur als zusätzliche Anmerkung: Es liegt auf der Hand, ob Ausnahmen tatsächlich böser sind als goto: Die gotoist auf eine einzelne Funktion beschränkt, was ein ziemlich kleiner Bereich ist, vorausgesetzt, die Funktion ist wirklich klein, die Ausnahme wirkt eher wie ein Kreuz aus gotound come from(siehe en.wikipedia.org/wiki/INTERCAL ;-)). Es kann so ziemlich zwei beliebige Codeteile verbinden, wobei möglicherweise einige dritte Funktionen übersprungen werden. Das Einzige, was es nicht gotokann, ist, zurückzukehren.
cmaster
2
@ PawełPrażak Bei der Arbeit mit vielen Funktionen höherer Ordnung sind Stapelspuren bei weitem nicht so wertvoll. Starke Garantien für Ein- und Ausgänge und die Vermeidung von Nebenwirkungen verhindern, dass diese Indirektion verwirrende Fehler verursacht.
Jack
11

Hier gibt es einige gute Antworten, aber ich denke, ein wichtiger Grund wurde nicht genug hervorgehoben: Wenn Ausnahmen auftreten, können Objekte in ungültigen Zuständen belassen werden. Wenn Sie eine Ausnahme "abfangen" können, kann Ihr Ausnahmebehandlungscode auf diese ungültigen Objekte zugreifen und mit ihnen arbeiten. Das wird furchtbar schief gehen, wenn der Code für diese Objekte nicht perfekt geschrieben wurde, was sehr, sehr schwierig ist.

Stellen Sie sich zum Beispiel vor, Sie implementieren Vector. Wenn jemand Ihren Vector mit einer Reihe von Objekten instanziiert, während der Initialisierung jedoch eine Ausnahme auftritt (beispielsweise beim Kopieren Ihrer Objekte in den neu zugewiesenen Speicher), ist es sehr schwierig, die Vector-Implementierung so zu codieren, dass Nr Speicher ist durchgesickert. Dieses kurze Papier von Stroustroup behandelt das Vector-Beispiel .

Und das ist nur die Spitze des Eisbergs. Was wäre, wenn Sie zum Beispiel einige, aber nicht alle Elemente kopiert hätten? Um einen Container wie Vector korrekt zu implementieren, müssen Sie fast jede Aktion, die Sie ausführen, rückgängig machen, sodass die gesamte Operation atomar ist (wie eine Datenbanktransaktion). Dies ist kompliziert und die meisten Anwendungen verstehen es falsch. Und selbst wenn es richtig gemacht wird, erschwert es den Implementierungsprozess des Containers erheblich.

Einige moderne Sprachen haben entschieden, dass es sich nicht lohnt. Rust hat zum Beispiel Ausnahmen, aber sie können nicht "abgefangen" werden, so dass Code nicht mit Objekten in einem ungültigen Zustand interagieren kann.

Charlie Flowers
quelle
1
Ziel des Catch ist es, ein Objekt konsistent zu machen (oder etwas zu beenden, wenn es nicht möglich ist), nachdem ein Fehler aufgetreten ist.
JoulinRouge
@ JoulinRouge Ich weiß. Einige Sprachen haben sich jedoch entschieden, Ihnen diese Gelegenheit nicht zu geben, sondern den gesamten Prozess zum Absturz zu bringen. Diese Sprachdesigner kennen die Art von Aufräumarbeiten, die Sie durchführen möchten, haben jedoch festgestellt, dass dies zu schwierig ist, und die damit verbundenen Kompromisse wären es nicht wert. Mir ist klar, dass Sie mit ihrer Wahl vielleicht nicht einverstanden sind ... aber es ist wertvoll zu wissen, dass sie die Wahl aus diesen besonderen Gründen bewusst getroffen haben.
Charlie Flowers
6

Eine Sache, die mich anfangs an der Rust-Sprache überrascht hat, ist, dass sie keine Fangausnahmen unterstützt. Sie können Ausnahmen auslösen , aber nur die Laufzeit kann sie abfangen, wenn eine Aufgabe (Think-Thread, aber nicht immer ein separater Betriebssystem-Thread) abstürzt. Wenn Sie eine Aufgabe selbst starten, können Sie fragen, ob sie normal beendet wurde oder nicht fail!().

Als solches ist es nicht failsehr oft idiomatisch . Die wenigen Fälle, in denen dies der Fall ist, sind zum Beispiel im Test-Harness (das nicht weiß, wie der Benutzercode aussieht), als oberste Ebene eines Compilers (die meisten Compiler geben stattdessen auf) oder beim Aufrufen eines Rückrufs auf Benutzereingabe.

Stattdessen wird häufig die ResultVorlage verwendet , um Fehler, die behandelt werden sollen, explizit weiterzugeben. Dies wird durch das try!Makro erheblich erleichtert , das um jeden Ausdruck gewickelt werden kann, der ein Ergebnis liefert und den erfolgreichen Arm liefert, wenn es einen gibt, oder auf andere Weise früh von der Funktion zurückkehrt.

use std::io::IoResult;
use std::io::File;

fn load_file(name: &Path) -> IoResult<String>
{
    let mut file = try!(File::open(name));
    let s = try!(file.read_to_string());
    return Ok(s);
}

fn main()
{
    print!("{}", load_file(&Path::new("/tmp/hello")).unwrap());
}
o11c
quelle
1
Ist es also fair zu sagen, dass auch dies (wie Go's Ansatz) Swift ähnelt, was hat assert, aber nein catch?
Orome
1
Versuchen Sie es in Swift! Das bedeutet: Ja, ich weiß, dass dies fehlschlagen könnte, aber ich bin mir sicher, dass dies nicht der Fall ist. Wenn dies fehlschlägt, ist mein Code falsch. In diesem Fall stürzen Sie bitte ab.
gnasher729
6

Meiner Meinung nach sind Ausnahmen ein wesentliches Instrument zur Erkennung von Codefehlern zur Laufzeit. Sowohl im Test als auch in der Produktion. Machen Sie ihre Nachrichten so ausführlich, dass Sie in Kombination mit einem Stack-Trace anhand eines Protokolls herausfinden können, was passiert ist.

Ausnahmen sind meist ein Entwicklungswerkzeug und eine Möglichkeit, in unerwarteten Fällen sinnvolle Fehlerberichte aus der Produktion zu erhalten.

Abgesehen von der Trennung von Bedenken (glücklicher Pfad mit nur erwarteten Fehlern vs. Durchfallen bis zum Erreichen eines generischen Handlers für unerwartete Fehler) ist es eine gute Sache, Ihren Code lesbarer und wartbarer zu machen, und es ist in der Tat unmöglich, Ihren Code für alles Mögliche vorzubereiten unerwartete Fälle, selbst wenn sie mit Fehlerbehandlungscode aufgebläht werden, um die Unlesbarkeit zu beenden.

Das ist eigentlich die Bedeutung von "unerwartet".

Übrigens, was erwartet wird und was nicht, ist eine Entscheidung, die nur am Einsatzort getroffen werden kann. Aus diesem Grund haben die überprüften Ausnahmen in Java nicht funktioniert - die Entscheidung wird zum Zeitpunkt der Entwicklung einer API getroffen, wenn überhaupt nicht klar ist, was erwartet oder unerwartet ist.

Einfaches Beispiel: Die API einer Hash-Map kann zwei Methoden haben:

Value get(Key)

und

Option<Value> getOption(key)

Die erste löst eine Ausnahme aus, wenn sie nicht gefunden wird. Die letztere gibt Ihnen einen optionalen Wert. In einigen Fällen ist letzteres sinnvoller, aber in anderen Fällen muss Ihr Code einfach davon ausgehen, dass ein Wert für einen bestimmten Schlüssel vorhanden ist. Wenn es also keinen gibt, ist dies ein Fehler, den dieser Code aufgrund eines grundlegenden Problems nicht beheben kann Annahme ist gescheitert. In diesem Fall ist es eigentlich das gewünschte Verhalten, aus dem Codepfad herauszufallen und auf einen generischen Handler zurückzugreifen, falls der Aufruf fehlschlägt.

Code sollte niemals versuchen, mit fehlgeschlagenen Grundannahmen umzugehen.

Natürlich nur, indem Sie sie überprüfen und gut lesbare Ausnahmen auslösen.

Ausnahmen zu werfen ist nicht böse, aber sie zu fangen kann es sein. Versuchen Sie nicht, unerwartete Fehler zu beheben. Fangen Sie Ausnahmen an einigen Stellen ab, an denen Sie eine Schleife oder Operation fortsetzen möchten, protokollieren Sie sie, melden Sie möglicherweise einen unbekannten Fehler, und fertig.

Fangblöcke überall sind eine sehr schlechte Idee.

Entwerfen Sie Ihre APIs auf eine Weise, die es Ihnen leicht macht, Ihre Absicht auszudrücken, dh zu erklären, ob Sie einen bestimmten Fall erwarten, z. B. Schlüssel nicht gefunden oder nicht. Benutzer Ihrer API können dann den auslösenden Aufruf nur für wirklich unerwartete Fälle auswählen.

Ich denke, der Grund, warum die Leute Ausnahmen ablehnen und zu weit gehen, indem sie dieses entscheidende Werkzeug für die Automatisierung der Fehlerbehandlung und die bessere Trennung von Bedenken von neuen Sprachen weglassen, sind schlechte Erfahrungen.

Das und einige Missverständnisse darüber, wozu sie eigentlich gut sind.

Wenn Sie sie durch ALLES durch monadische Bindung simulieren, ist Ihr Code weniger lesbar und kann nicht mehr gewartet werden, und Sie haben keine Stack-Ablaufverfolgung, was diesen Ansatz erheblich verschlechtert.

Die Fehlerbehandlung im funktionalen Stil eignet sich hervorragend für erwartete Fehlerfälle.

Lassen Sie die Ausnahmebehandlung automatisch den Rest erledigen, das ist es, wofür es ist :)

yeoman
quelle
3

Swift verwendet hier die gleichen Prinzipien wie Objective-C, nur konsequenter. Ausnahmen in Objective-C weisen auf Programmierfehler hin. Sie werden nur von Crash-Reporting-Tools verarbeitet. "Ausnahmebehandlung" erfolgt durch Fixieren des Codes. (Es gibt einige ahem Ausnahmen. Zum Beispiel in der Kommunikation zwischen Prozessen. Aber das ist ziemlich selten und viele Leute stoßen nie darauf. Und Objective-C hat tatsächlich try / catch / finally / throw, aber Sie verwenden sie selten). Swift hat gerade die Möglichkeit entfernt, Ausnahmen zu fangen.

Swift hat eine Funktion, die wie eine Ausnahmebehandlung aussieht , aber nur eine erzwungene Fehlerbehandlung darstellt. In der Vergangenheit hatte Objective-C ein weit verbreitetes Fehlerbehandlungsmuster: Eine Methode gab entweder ein BOOL (JA für Erfolg) oder eine Objektreferenz (null für Fehler, nicht null für Erfolg) zurück und hatte einen Parameter "Zeiger auf NSError *" In diesem Fall wird eine NSError-Referenz gespeichert. Swift konvertiert Aufrufe einer solchen Methode automatisch in eine Art Ausnahmebehandlung.

Im Allgemeinen können Swift-Funktionen problemlos Alternativen zurückgeben, z. B. ein Ergebnis, wenn eine Funktion ordnungsgemäß funktioniert hat, und einen Fehler, wenn sie fehlgeschlagen ist. Das erleichtert die Fehlerbehandlung erheblich. Aber die Antwort auf die ursprüngliche Frage: Die Swift-Designer waren offensichtlich der Meinung, dass es einfacher ist, eine sichere Sprache zu erstellen und erfolgreichen Code in einer solchen Sprache zu schreiben, wenn die Sprache keine Ausnahmen enthält.

gnasher729
quelle
Für Swift ist dies die richtige Antwort, IMO. Swift muss mit den vorhandenen Objective-C-System-Frameworks kompatibel bleiben, sodass es unter der Haube keine traditionellen Ausnahmen gibt. Ich schrieb vor einiger Zeit
uliwitness
2
 int result;
 if((result = operation_that_can_throw_ioerror()) == IOError)
 {
  handle_the_exception_somehow();
 }
 else
 {
   # we don't want to catch the IOError if it's raised
   result = another_operation_that_can_throw_ioerror();
 }
 result |= something_we_always_need_to_do();
 return result;

In C würden Sie mit so etwas wie dem oben genannten enden.

Gibt es Dinge, die ich in Swift nicht machen kann, die ich mit Ausnahmen machen kann?

Nein, da ist nichts. Am Ende behandeln Sie nur Ergebniscodes anstelle von Ausnahmen.
Mit Ausnahmen können Sie Ihren Code neu organisieren, sodass die Fehlerbehandlung nicht mehr mit Ihrem Code für einen zufriedenen Pfad zu tun hat.

Steinmetalle
quelle
Und ebenso diese Aufrufe, ...throw_ioerror()Fehler zurückzugeben, anstatt Ausnahmen zu werfen?
Orome
1
@raxacoricofallapatorius Wenn wir behaupten, dass es keine Ausnahmen gibt, gehe ich davon aus, dass das Programm dem üblichen Muster folgt, Fehlercodes bei einem Fehler und 0 bei einem Erfolg zurückzugeben.
Stonemetal
1
@stonemetal Einige Sprachen, wie Rust und Haskell, verwenden das Typensystem, um etwas aussagekräftigeres als einen Fehlercode zurückzugeben, ohne versteckte Exit-Punkte hinzuzufügen, wie dies bei Ausnahmen der Fall ist. Eine Rust-Funktion kann beispielsweise eine Result<T, E>Aufzählung zurückgeben, die entweder eine Ok<T>oder eine sein kann Err<E>, Twobei der Typ, den Sie haben wollten, Eder Typ ist, der einen Fehler darstellt. Pattern Matching und einige spezielle Methoden vereinfachen die Behandlung von Erfolgen und Fehlern. Kurz gesagt, nicht davon ausgehen, dass das Fehlen von Ausnahmen automatisch Fehlercodes bedeutet.
8bittree,
1

Zusätzlich zur Antwort von Charlie:

Diese Beispiele für deklarierte Ausnahmebehandlung, die Sie in vielen Handbüchern und Büchern finden, sehen nur an sehr kleinen Beispielen sehr schlau aus.

Auch wenn Sie das Argument eines ungültigen Objektzustands beiseite lassen, verursachen sie beim Umgang mit einer großen App immer große Schmerzen.

Wenn Sie sich zum Beispiel mit E / A beschäftigen müssen und eine Kryptografie verwenden, können Sie 20 Arten von Ausnahmen haben, die von 50 Methoden geworfen werden können. Stellen Sie sich vor, wie viel Code für die Ausnahmebehandlung Sie benötigen. Die Ausnahmebehandlung benötigt mehr Code als der Code selbst.

In der Realität wissen Sie, wann eine Ausnahme nicht auftreten kann und Sie müssen nie so viel Ausnahmebehandlung schreiben, sodass Sie nur einige Problemumgehungen verwenden, um deklarierte Ausnahmen zu ignorieren. In meiner Praxis müssen nur etwa 5% der deklarierten Ausnahmen im Code behandelt werden, um eine zuverlässige App zu erhalten.

konmik
quelle
Nun, in der Realität können diese Ausnahmen oft nur an einer Stelle behandelt werden. Wenn beispielsweise bei einer Funktion zum Herunterladen von Daten die Aktualisierung fehlschlägt oder der DNS nicht auflösbar ist oder der Webserver eine 404 zurückgibt, spielt es keine Rolle, fangen Sie sie oben ab und melden Sie den Fehler dem Benutzer.
Zan Lynx