Zgłaszanie i wyławianie wyjątków w tej samej funkcji / metodzie

10

Napisałem funkcję, która prosi użytkownika o wprowadzenie danych, dopóki użytkownik nie wprowadzi dodatniej liczby całkowitej (liczby naturalnej). Ktoś powiedział, że nie powinienem wyrzucać i wychwytywać wyjątków w mojej funkcji i pozwolić, aby wywołujący moją funkcję je obsługiwał.

Zastanawiam się, co sądzą o tym inni programiści. Prawdopodobnie niewłaściwie wykorzystuję wyjątki w funkcji. Oto kod w Javie:

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;
}

Interesują mnie dwie rzeczy:

  1. Czy powinienem pozwolić rozmówcy martwić się o wyjątki? Istotą tej funkcji jest to, że dręczy użytkownika, dopóki użytkownik nie wprowadzi liczby naturalnej. Czy punkt funkcji jest zły? Nie mówię o interfejsie użytkownika (użytkownik nie jest w stanie wydostać się z pętli bez odpowiedniego wejścia), ale o zapętlonym wejściu z obsługiwanymi wyjątkami.
  2. Czy powiedziałbyś, że instrukcja throw (w tym przypadku) jest niewłaściwym wykorzystaniem wyjątków? Mogę łatwo utworzyć flagę do sprawdzania poprawności numeru i wypisać komunikat ostrzegawczy na podstawie tej flagi. Ale to dodałoby więcej linii do kodu i myślę, że jest on w pełni czytelny.

Rzecz w tym, że często piszę osobną funkcję wejściową. Jeśli użytkownik musi wprowadzić liczbę wiele razy, tworzę osobną funkcję wprowadzania, która obsługuje wszystkie wyjątki i ograniczenia formatowania.

usr
źródło
Bardzo zależy od języka. Niektóre języki korzystają z wyjątków bardziej swobodnie niż inne.
Martin York,

Odpowiedzi:

11

Wyjątkiem jest to, że pozwala metodzie powiedzieć dzwoniącemu, który wszedł w stan, w którym nie mógł normalnie kontynuować, bez zmuszania użytkownika do osadzenia kodu błędu w wartości zwracanej.

W twoim przypadku twoja metoda dokładnie wie, co robić, gdy wartość wejściowa nie jest większa niż 0. Jedynym powodem, dla którego zapisujesz tutaj wiersze, jest to, że zdarza się, że generujesz ten sam wyjątek, jaki byś otrzymał, gdyby dane wejściowe nie były liczbą. Jednak wyjątek, który zgłaszasz, nie pokazuje poprawnie, dlaczego twój kod nie lubi danych wejściowych. Gdyby ktoś inny przyszedł i zobaczył ten kod, musiałby poświęcić dodatkowy czas na sprawdzenie, jak dokładnie działa.

unholysampler
źródło
Tak, dlatego myślałem, że to niewłaściwe użycie. Tak więc, ignorując tę ​​instrukcję throw, zgadzasz się, że można obsługiwać wyjątki w takiej pętli wewnątrz funkcji zamiast pozwolić, aby osoba dzwoniąca je złapała? Instrukcja catch jest dostępna z powodu Integer.parseInt (ponownie, ignorując rzut).
usr
1
@usr: Ta odpowiedź zależy znacznie bardziej od tego, jak reszta systemu ma ze sobą współpracować. W pewnym momencie należy rozwiązać wyjątki. Istnieje kilka różnych sposobów na zorganizowanie go i jest to jeden z nich. To, co jest najlepsze, zależy od innych informacji, których tu nie mamy.
unholysampler
4

To złe użycie wyjątków. Na początek liczba dodatnia nie jest wyjątkiem formatu.

Po co w ogóle korzystać z wyjątków? Jeśli wiesz, które dane wejściowe są niedozwolone, po prostu nie wychodź z pętli, dopóki nie uzyskasz prawidłowych danych wejściowych od użytkownika, coś takiego:

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
źródło
Integer.intParse zgłasza wyjątki formatu, więc użycie instrukcji catch jest całkowicie poprawne. Chcę przede wszystkim wiedzieć, czy można używać instrukcji catch w pętli w funkcji, czy też powinienem pozwolić, aby funkcja wywołująca obsługiwała wyjątek formatu.
usr
Integer.parseInt()zgłasza a, NumberFormatExceptionjeśli nie może przeanalizować podanego argumentu ciągu. Nie ma potrzeby, abyś sam (i nie będziesz w stanie) sam zgłosić wyjątku.
Bernard,
Zredagowałem przykład kodu, aby był bardziej wyraźny.
Bernard
„i nie będziesz w stanie) samodzielnie wyrzucić wyjątku” - co masz na myśli?
Michael Borgwardt
@Michael Borgwardt: Mam na myśli, że skoro Integer.parseInt()metoda rzuci za ciebie wyjątek, jeśli wystąpi, nie będziesz mógł go później wyrzucić, ponieważ został już wyrzucony.
Bernard,
1

Łap wyjątki tylko wtedy, gdy zamierzasz zrobić coś, co jest istotne dla bieżącego wywołania metody; tj. czyszczenie, logika awarii itp. W tym przypadku catch po prostu wysyła komunikat do konsoli, nie ma znaczenia dla metody sideInput, dlatego może być obsługiwany dalej w łańcuchu połączeń / stosie.

Tutaj możesz pozbyć się try / catch i po prostu udokumentować wywołanie metody:

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

Nadal trzeba poradzić sobie z tym wyjątkiem w łańcuchu połączeń / stosie!

Jon Raynor
źródło
1
Wiadomość jest tylko dla lepszego interfejsu użytkownika. Istotą tej funkcji jest to, że nakłania użytkownika do wprowadzania danych, dopóki nie wprowadzi prawidłowego wejścia. Nie można tego zrobić bez bloków try-catch. Moje pytanie dotyczyło tego, czy sam punkt jest ważny. Myślę, że tak, ale ktoś mi powiedział, że powinienem usunąć bloki try-catch i pozwolić abonentowi wywołującemu ten wyjątek. Ale wtedy funkcja nie będzie działać zgodnie z przeznaczeniem.
usr
1

Nie powinieneś rzucać i wychwytywać tego samego wyjątku w metodzie, nawet myślę, że blok przechwytuje ten sam wyjątek, który rzucasz, więc tak naprawdę go nie rzucasz.

Jeśli się parseIntudało, to nie jest NumberFormatException.

jeśli strona jest mniejsza niż zero, powinieneś rzucić NegativeSideLengthException;

Utwórz niestandardowy / biznesowy wyjątek o nazwie NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

Następnie sideInputzgłasza 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;
}

Możesz nawet (jeśli chcesz) dodać kolejny blok przechwytywania, aby złapać NegativeSideLengthExceptioni nie ma metody rzucić go.

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);

Flagi nie są dobrym sposobem radzenia sobie z wyjątkami.

Tulains Córdova
źródło
-1

Wyjątki to koszmarne rzeczy, przynoszą więcej złożoności niż rozwiązują.

Po pierwsze, jeśli nie złapiesz wyjątków, dzwoniący może tylko to zrobić on error resume next, to znaczy po tygodniu nawet ty nie będziesz wiedział, co twoja funkcja może rzucić i co z tym zrobić:

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

Zasadniczo, jeśli je złapiesz, musisz bardzo dobrze rozumieć umowy i gwarancje wyjątków. To rzadko zdarza się w prawdziwym świecie. A także twój kod będzie trudny do odczytania.

Zabawne jest również to, że jeśli naprawdę masz szansę poradzić sobie z wyjątkami, potrzebujesz prawdziwego języka RAII, co jest dość zabawne, ponieważ Java i .NET to wyjątki ...

Powtarzam to jeszcze raz, ale ...:

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

Koder
źródło
4
-1 za wysłanie rantu granicznego, pełnego niezupełnie poprawnych - lub przynajmniej zależnych od kontekstu / języka - wypowiedzi, zamiast odpowiedzi na rzeczywiste pytanie OP.
Péter Török
@ PéterTörök: „Daj człowiekowi rybę, a będziesz go karmić przez jeden dzień. Naucz mężczyznę, aby łowił ryby i karmisz go przez całe życie.” Wyjątki są bardzo poważne i ludzie powinni je znać -1000 / + 1, nie obchodzi mnie to.
Coder
1
Możesz śmiało wierzyć, że możesz łatwiej pisać poprawne kody bez wyjątków. Po prostu nie podawaj swoich poglądów i przekonań jako faktów (nawet jeśli Joel jest tego samego zdania, wciąż jest opinią, a nie faktem) i nie publikuj nieistotnych odpowiedzi na SE.
Péter Török
@ PéterTörök: To nie jest opinia, to fakt, za każdym razem, gdy używasz komponentu, który zgłasza wyjątek wewnętrznie, musisz przeszukiwać całą hierarchię i sprawdzać każdy wiersz kodu, aby wiedzieć, co złapać, a jeśli komponenty oferują silne gwarancja, a wszystko jest wycofywane lub jeśli ten haczyk to tylko fałszywe poczucie bezpieczeństwa. Cholera, nie znasz nawet wszystkich wyjątków rzucania std :: string, możesz sprawdzić specyfikacje, ale nigdy ich nie znajdziesz. Rzeczy takie : throw(thisexception, thatexception)są wręcz błędne i nigdy nie powinny być używane, ponieważ w przeciwnym razie dostaniesz nieoczekiwany wyjątek.
Koder
2
OK, a co powiesz na kod, który nie używa wyjątków? Ojej, musisz przeszukiwać kod, aby sprawdzić każdy wiersz i sprawdzić, czy zwracane wartości są obsługiwane poprawnie, czy ignorowane. A gdy wartość zwracana jest ignorowana, nikt nie zauważy, dopóki aplikacja nie zawiesi się kilka tysięcy linii później. Wyjątki przynajmniej zmuszają cię do zwrócenia uwagi. Tak, możesz je połknąć - ale tylko w sposób wyraźny catch. Podczas gdy zignorowana wartość zwracana jest niemożliwa do przeszukania - można ją zidentyfikować tylko poprzez przegląd kodu po linii. Na koniec, konstruktory nie mają wartości zwracanych, jest to główny powód do stosowania wyjątków.
Péter Török