Ausnahmen in derselben Funktion / Methode auslösen und abfangen

10

Ich habe eine Funktion geschrieben, die einen Benutzer zur Eingabe auffordert, bis der Benutzer eine positive Ganzzahl (eine natürliche Zahl) eingibt. Jemand sagte, ich sollte keine Ausnahmen in meiner Funktion auslösen und abfangen und den Aufrufer meiner Funktion damit umgehen lassen.

Ich frage mich, was andere Entwickler darüber denken. Ich missbrauche wahrscheinlich auch Ausnahmen in der Funktion. Hier ist der Code in Java:

private static int sideInput()
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                // probably a misuse of exceptions
                throw new NumberFormatException();
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Ich interessiere mich für zwei Dinge:

  1. Sollte ich den Anrufer über Ausnahmen sorgen lassen? Der Punkt der Funktion ist, dass sie den Benutzer nervt, bis der Benutzer eine natürliche Zahl eingibt. Ist der Punkt der Funktion schlecht? Ich spreche nicht von der Benutzeroberfläche (Benutzer kann ohne ordnungsgemäße Eingabe nicht aus der Schleife herauskommen), sondern von Schleifeneingaben mit Ausnahmen.
  2. Würden Sie sagen, dass die throw-Anweisung (in diesem Fall) ein Missbrauch von Ausnahmen ist? Ich könnte leicht ein Flag zum Überprüfen der Gültigkeit der Nummer erstellen und die Warnmeldung basierend auf diesem Flag ausgeben. Aber das würde dem Code mehr Zeilen hinzufügen und ich denke, er ist so wie er ist perfekt lesbar.

Die Sache ist, ich schreibe oft eine separate Eingabefunktion. Wenn der Benutzer eine Zahl mehrmals eingeben muss, erstelle ich eine separate Funktion für die Eingabe, die alle Formatierungsausnahmen und -einschränkungen behandelt.

usr
quelle
Kommt sehr auf die Sprache an. Einige Sprachen verwenden Ausnahmen freier als andere.
Martin York

Antworten:

11

Der Punkt einer Ausnahme ist, dass eine Methode dem Aufrufer mitteilen kann, der in einen Zustand eingetreten ist, in dem er nicht normal fortgesetzt werden konnte, ohne dass Sie gezwungen sind, einen Fehlercode in den Rückgabewert einzubetten.

In Ihrem Fall weiß Ihre Methode genau, was zu tun ist, wenn die Eingabe nicht größer als 0 ist. Der einzige Grund, warum Sie hier Zeilen speichern, besteht darin, dass Sie zufällig dieselbe Ausnahme auslösen, die Sie erhalten würden, wenn die Eingabe keine Zahl wäre. Die Ausnahme, die Sie auslösen, zeigt jedoch nicht richtig an, warum Ihr Code die Eingabe nicht mag. Wenn jemand anderes mitkommen und diesen Code sehen würde, müsste er zusätzliche Zeit damit verbringen, genau zu sehen, wie die Dinge funktionieren.

unholysampler
quelle
Ja, deshalb dachte ich, es sei ein Missbrauch. Wenn Sie diese throw-Anweisung ignorieren, stimmen Sie zu, dass es in Ordnung ist, Ausnahmen in einer solchen Schleife innerhalb einer Funktion zu behandeln, anstatt sie vom Aufrufer abfangen zu lassen? Die catch-Anweisung ist aufgrund von Integer.parseInt vorhanden (wobei der Wurf erneut ignoriert wird).
Usr
1
@usr: Diese Antwort hängt viel mehr davon ab, wie der Rest des Systems zusammenarbeiten soll. Ausnahmen müssen irgendwann behandelt werden. Es gibt verschiedene Möglichkeiten, wie Sie es organisieren können, und dies ist eine davon. Welches am besten ist, hängt von anderen Informationen ab, die wir hier nicht haben.
Unholysampler
4

Dies ist eine schlechte Verwendung von Ausnahmen. Zunächst einmal ist eine nicht positive Zahl keine Formatausnahme.

Warum überhaupt Ausnahmen verwenden? Wenn Sie wissen, welche Eingabe nicht zulässig ist, brechen Sie die Schleife erst ab, wenn Sie vom Benutzer eine gültige Eingabe erhalten, etwa wie folgt:

while (true)
{
   // Get user input.
   String input = scanner.nextLine();

   try
   {
      side = Integer.parseInt(input);

      break;
   }
   catch (NumberFormatException ex)
   {
      // Inform user of invalid input.
      System.out.println("Invalid input. Enter a natural number.");
   }
}
Bernard
quelle
Integer.intParse löst Formatausnahmen aus, sodass die Verwendung der catch-Anweisung vollkommen gültig ist. Ich möchte hauptsächlich wissen, ob es in Ordnung ist, die catch-Anweisung in einer Schleife in einer Funktion zu verwenden, oder ob ich den Aufrufer der Funktion die Formatausnahme behandeln lassen soll.
usr
Integer.parseInt()löst ein aus, NumberFormatExceptionwenn das angegebene String-Argument nicht analysiert werden kann. Sie müssen die Ausnahme nicht selbst auslösen (und Sie werden es auch nicht können).
Bernard
Ich habe das Codebeispiel so bearbeitet, dass es expliziter ist.
Bernard
"und du wirst nicht in der Lage sein) die Ausnahme selbst zu werfen" - was meinst du dort?
Michael Borgwardt
@ Michael Borgwardt: Ich meine, da die Integer.parseInt()Methode die Ausnahme für Sie auslöst , wenn sie auftritt, können Sie sie danach nicht selbst auslösen, da sie bereits ausgelöst wurde.
Bernard
1

Ausnahmen nur abfangen, wenn Sie etwas tun möchten, das für den aktuellen Methodenaufruf relevant ist. dh Bereinigung, Fehlerlogik usw. In diesem Fall sendet der Catch lediglich eine Nachricht an die Konsole. Dies ist für die sideInput-Methode nicht relevant, sodass er weiter oben in der Aufrufkette / im Aufrufstapel verarbeitet werden kann.

Man kann das try / catch hier loswerden und einfach den Methodenaufruf dokumentieren:

//Throws NumberFormatException if read input is less than 0
private static int sideInput()

Diese Ausnahme muss noch weiter oben in der Aufrufkette / im Stack behandelt werden!

Jon Raynor
quelle
1
Die Nachricht dient nur einer besseren Benutzeroberfläche. Der Punkt der Funktion besteht darin, dass der Benutzer zur Eingabe aufgefordert wird, bis er eine gültige Eingabe eingibt. Dies ist ohne Try-Catch-Blöcke nicht möglich. Meine Frage war, ob der Punkt selbst gültig ist. Ich denke schon, aber jemand sagte mir, ich sollte die Try-Catch-Blöcke entfernen und den Anrufer diese spezielle Ausnahme behandeln lassen. Aber dann funktioniert die Funktion nicht wie vorgesehen.
Usr
1

Sie sollten in einer Methode nicht dieselbe Ausnahme auslösen und abfangen. Ich denke sogar, dass der catch-Block dieselbe Ausnahme abfängt, die Sie auslösen, sodass Sie sie nicht wirklich auslösen.

Wenn parseIntes erfolgreich war, dann ist es kein NumberFormatException.

Wenn die Seite kleiner als Null ist, sollten Sie a werfen NegativeSideLengthException.

Erstellen Sie eine benutzerdefinierte / geschäftliche Ausnahme mit dem Namen NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


    public NegativeSideLengthException(Integer i)
    {
        super("Invalid negative side length "+i);        
    }

}

Dann sideInputwirft NegativeSideLengthException

private static int sideInput() throws NegativeSideLengthException
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                throw new NegativeSideLengthException(side);
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Sie können sogar (wenn Sie möchten) einen weiteren catch-Block hinzufügen, um ihn zu fangen, NegativeSideLengthExceptionohne dass die Methode ihn wirft.

do {
    System.out.print("Side length: ");
    input = scanner.nextLine();
    try {
        side = Integer.parseInt(input);
        if (side <= 0) {
            throw new NegativeSideLengthException(side);
        }
    }
    catch (NumberFormatException numFormExc) {
        System.out.println("Invalid input. Enter a natural number.");
    } catch (NegativeSideLengthException e){
        System.out.println("Invalid input. Enter a non-negative number.");
    }
} while (side <= 0);

Flags sind keine gute Möglichkeit, mit Ausnahmen umzugehen.

Tulains Córdova
quelle
-1

Ausnahmen sind ziemlich albtraumhafte Dinge, sie bringen mehr Komplexität mit sich als sie lösen.

Erstens, wenn Sie Ihre Ausnahmen nicht abfangen, kann der Anrufer dies nur tun on error resume next, dh nach einer Woche wissen selbst Sie nicht, was Ihre Funktion auslösen kann und was Sie damit tun sollen:

{
    ...
}
catch(OutOfMemory, CorruptedMemory, BadData, DanglingPointers, UnfinishedCommit)
{
    Console.WriteLine("Nothing to see here, move on.");
    Console.WriteLine("The app is very stable, see, no crashing!");
}

Grundsätzlich muss man, wenn man sie fängt, ein sehr gutes Verständnis für Verträge und Ausnahmegarantien haben. Das passiert in einer realen Welt selten. Und auch Ihr Code wird schwer zu lesen sein.

Das Lustige ist auch, dass Sie, wenn Sie wirklich die Chance haben, Ausnahmen zu behandeln, eine echte RAII-Sprache benötigen, die irgendwie humorvoll ist, da es bei Java und .NET nur um Ausnahmen geht ...

Wiederholen Sie dies noch einmal, aber ...:

http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

http://www.joelonsoftware.com/items/2003/10/13.html

Codierer
quelle
4
-1 für die Veröffentlichung eines Borderline-Rants, der voll von nicht ganz korrekten - oder zumindest kontext- / sprachabhängigen - Aussagen ist, anstatt die eigentliche Frage des OP zu beantworten.
Péter Török
@ PéterTörök: "Gib einem Mann einen Fisch und du fütterst ihn für einen Tag. Bringe einem Mann das Fischen bei und du fütterst ihn ein Leben lang." Es gibt sehr ernsthafte Probleme hinter Ausnahmen, und die Leute sollten sie kennen -1000 / + 1, es ist mir eigentlich egal.
Coder
1
Sie können gerne glauben, dass Sie korrekten Code einfacher als mit Ausnahmen schreiben können. Geben Sie Ihre Ansichten und Überzeugungen nur nicht als Fakten an (auch wenn Joel der gleichen Meinung ist, ist es immer noch eine Meinung, keine Tatsache), und veröffentlichen Sie keine irrelevanten Antworten auf SE.
Péter Török
@ PéterTörök: Es ist keine Meinung, es ist eine Tatsache, dass Sie jedes Mal, wenn Sie eine Komponente verwenden, die intern eine Ausnahme auslöst, die gesamte Hierarchie crawlen und jede einzelne Codezeile überprüfen müssen, um zu wissen, was zu fangen ist und ob die Komponenten stark sind Garantie, und alles wird zurückgesetzt, oder wenn dieser Haken nur ein falsches Sicherheitsgefühl ist. Heck, Sie kennen nicht einmal alle Ausnahmen, die std :: string auslöst. Sie können die Spezifikationen nachschlagen, aber Sie werden sie nie finden. Dinge wie : throw(thisexception, thatexception)sind völlig falsch und sollten niemals verwendet werden, da Sie sonst unerwartet werden, außer.
Coder
2
OK, wie wäre es also mit Code, der keine Ausnahmen verwendet? Gee, Sie müssen durch den Code kriechen, um jede einzelne Zeile zu überprüfen, um festzustellen, ob Rückgabewerte ordnungsgemäß behandelt oder ignoriert werden. Und wenn ein Rückgabewert ignoriert wird, merkt es niemand, bis Ihre App möglicherweise ein paar tausend Zeilen später abstürzt. Ausnahmen zwingen Sie zumindest zur Kenntnisnahme. Ja, Sie können sie schlucken - aber nur mit einem expliziten catch. Ein ignorierter Rückgabewert ist zwar nicht durchsuchbar, kann jedoch nur durch zeilenweise Codeüberprüfung identifiziert werden. Last but not least haben Konstruktoren keine Rückgabewerte. Dies ist der Hauptgrund für die Verwendung von Ausnahmen.
Péter Török