Czy to jest poprawne rzucenie wyjątku?

0

Witam

Mam pytanie odnośnie wyrzucania wyjątku przy nieprawidłowej liczbie argumentów.


/**
 *
 * @author 
 */
public class MainControl {
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        try{
            if (args.length==0) {
                throw new NoArgumentException();
            }
            if (args.length>1) {
                throw new TooManyArgumentException();
            }
               MenuControl enter = new MenuControl(Integer.parseInt(args[0]));
               enter.mainMenuDisplay();
           }
        catch (IOException e) 
        {
           //
        }
        catch (NumberFormatException e) 
         {
           //
         }
        catch (NoArgumentException e)
        {
}
        catch (TooManyArgumentException e)
        {}
   }
}

Czy nie jest to "'rzucanie' wyjątku bezpośrednio w sekcji try i jego natychmiastowa obsługa" ?

1

Prawidłowość obsługi wyjątku zależy w dużym stopniu od tego czy w danym miejscu programu jesteś w stanie cokolwiek na to poradzić. Obowiązuje tutaj zasada "declare or handle" czyli albo obsługujesz wyjątek wykonując jakieś działania w bloku catch (w miejscu wystąpienia wyjątku) albo deklarujesz metodę z użyciem "throws SomeException". W ten sposób wymuszasz obsługę wyjątku wyżej. W tej konkretnej sytuacji wewnątrz funkcji main nic z liczbą przekazanych argumentów nie zrobisz a funkcji wyżej nie ma więc ciężko się ustosunkować.

Zawsze jak masz wątpliwości to zadaj sobie pytania:

  • Czy możesz cokolwiek na to poradzić ?
  • Czy możesz powtórzyć operację ?
  • Czy musisz inne wycofać ?
  • Czy mogę to komu innemu zwalić na głowę ?

Zwalanie na innych jest dobre bo rzadko kiedy decyzja o rozprawieniu się z błędem który wystąpił bezpośrednio może zostać podjęta lokalnie – błąd dostępu do bazy danych musi zostać obsłużony w warstwie biznesowej a nie persystencji - W razie wątpliwości – rzuć wyżej.

Wnioski i zalecenia

  • Kochaj albo rzuć :)

  • Staraj się nie podejmować decyzji co zrobić z błędem – w miarę możliwości zwal całą odpowiedzialność na kod wyżej

  • Zachowaj enkapsulację: Na przykład z warstwy persystencji wyżej nie powinien wychodzić SQLException – poczytaj na temat Exception Chaining

1

Jest źle.

  1. Wyjątki NoArgumentException i TooManyArgumentException nie powinny w ogóle być wyrzucane w tym przypadku. W momencie gdy masz nieprawidłową ilość argumentów to powinieneś od razu przejść do obsługi takiego stanu. Nie jest on WYJĄTKOWY, użytkownik może, i na pewno się pomyli. Wyjątek możesz rzucić jeżeli cały mechanizm obsługi błędów jest umieszczony w innej warstwie.
  2. Wyjątek IOException jest zbyt ogólny. Zastąp go własnym wyjątkiem, który będzie coś mówił.
  3. Wyjątek NumberFormatException powinien zostać przechwycony i to jest ok, ale znowuż nie masz tu warstwy obsługi.
public class MainControl {
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            validateInput(args);
            int input = parseInput(args[0]);
            MenuControl enter = new MenuControl(input);
            enter.mainMenuDisplay();
        } catch (IOException | NoArgumentException | TooManyArgumentException | ParseException e) {
            ExceptionHandler.handle(e);
        }
    }
//..
}

W tej wersji masz kod podzielony na warstwy. Warstwa niższa warstwa sprawdza warunki i jak trafia na błąd to wyrzuca wyjątek. Niech ten kto wywołał się martwi.
ExceptionHandler.handle(e) obsługuje wyjątek z godnie z ogólną polityką.

I trochę inne podejście

public class MainControl {
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        RunInSecure.runThis(() -> {
            validateInput(args);
            int input = parseInput(args[0]);
            MenuControl enter = new MenuControl(input);
            enter.mainMenuDisplay();
            return null;
        }, new ExceptionHandler(), null);
    }

}

class RunInSecure {
    public static <T> T runThis(Callable<T> callable, ExceptionHandler handler, T def) {
        try {
            return callable.call();
        } catch (Exception e) {
            handler.handle(e);
        }
        return def;
    }
}

w tym przypadku delegujemy uruchomienie kodu do osobnego obiektu, który będzie zajmować się obsługą błędów jak by coś się wysypało. Można to jeszcze delikatnie podrasować, ale to już będzie zbyt skomplikowane rozwiązanie jak na taki kod.

1 użytkowników online, w tym zalogowanych: 0, gości: 1