Метання та вилучення виключень у тій же функції / методі


10

Я написав функцію, яка запитує у користувача введення, поки користувач не введе додатне ціле число (натуральне число). Хтось сказав, що я не повинен кидати і виловлювати винятки у своїй функції, а дозволити абоненту моєї функції керувати ними.

Цікаво, що думають з цього приводу інші розробники. Я також, мабуть, зловживаю винятками у функції. Ось код на 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;
}

Мене цікавлять дві речі:

  1. Чи варто дозволити абоненту перейматися винятками? Сенс функції полягає в тому, що він нудить користувача, поки користувач не введе натуральне число. Чи поганий пункт функції? Я не говорю про інтерфейс користувача (користувач не може вийти з циклу без належного введення), а про циклічне введення з обробкою винятків.
  2. Ви б сказали, що заява про кидок (у даному випадку) - це неправильне використання винятків? Я міг легко створити прапор для перевірки дійсності номера та вивести попереджувальне повідомлення на основі цього прапора. Але це додало б більше рядків до коду, і я думаю, що це ідеально читабельно, як є.

Річ у тому, що я часто пишу окрему функцію введення. Якщо користувачеві доводиться вводити число кілька разів, я створюю окрему функцію для введення, яка обробляє всі винятки та обмеження форматування.


Сильно залежить від мови. Деякі мови використовують винятки вільніше, ніж інші.
Мартін Йорк

Відповіді:


11

Суть винятку полягає в тому, що він дозволяє методу сказати тому, хто викликав стан, який ввійшов у стан, коли він не міг продовжуватись нормально, не змушуючи вас вставляти код помилки у повернене значення.

У вашому випадку ваш метод точно знає, що робити, коли вхід не перевищує 0. Єдина причина, коли ви зберігаєте рядки тут - це те, що ви, здається, кидаєте той самий виняток, який ви отримали, якби введення не було числом. Однак виняток, який ви кидаєте, не відповідає належним чином, чому ваш код не любить введення. Якби хтось інший прийшов і побачив цей код, їм доведеться витратити додатковий час, намагаючись зрозуміти, як саме працюють.


Так, саме тому я подумав, що це неправильне використання. Отже, ігноруючи цю заяву кидка, ви погоджуєтесь, що нормально обробляти винятки в такому циклі всередині функції, а не дозволяти абоненту їх вловлювати? Заява улову існує через Integer.parseInt (знову ігноруючи кидок).
usr

1
@usr: Ця відповідь набагато більше залежить від того, як решта системи повинна працювати разом. Винятки потрібно вирішити в певний момент. Є кілька різних способів, як ви могли б це організувати, і це один із них. Що найкраще залежить від іншої інформації, якої ми тут не маємо.
unholysampler

4

Це погане використання винятків. Для початку непозитивне число не є винятком формату.

Навіщо взагалі використовувати винятки? Якщо ви знаєте, що введення заборонено, просто не виривайте з циклу, поки ви не отримаєте дійсне введення від користувача, щось на зразок наступного:

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.");
   }
}

Integer.intParse видає винятки у форматі, тому використання оператора catch є цілком правильним. В основному я хочу знати, чи нормально використовувати оператор catch у циклі у функції чи повинен дозволити виклику функції обробляти виключення формату.
usr

Integer.parseInt()кидає a, NumberFormatExceptionякщо він не може розібрати наданий рядок аргумент. Немає потреби (і ви не зможете) викидати виняток самостійно.
Бернар

Я відредагував приклад коду, щоб він був більш чітким.
Бернард

"і ви не зможете) кинути виняток самостійно" - що ви там маєте на увазі?
Майкл Боргвардт

@Michael Borgwardt: Я маю на увазі, що оскільки Integer.parseInt()метод викине для вас виняток, якщо він відбудеться, ви не зможете кинути його згодом, оскільки його вже кинули.
Бернард

1

Виловлюйте винятки, лише якщо ви маєте намір зробити щось, що відповідає поточному виклику методу; тобто очищення, логіка відмов і т.д.

Тут можна позбутися від параметра try / catch і просто задокументувати виклик методу:

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

Ще потрібно обробляти цей виняток далі в ланцюзі / стеку викликів!


1
Повідомлення є лише для кращого інтерфейсу користувача. Сенс функції полягає в тому, що він нудить користувача на введення, поки він / він не введе дійсний вхід. Це неможливо зробити без блоків спробу лову. Моє запитання полягало у тому, чи справедлива сама точка. Я думаю, що це так, але хтось сказав мені, що я повинен видалити блоки пробного захоплення і дозволити абоненту обробляти цей виняток. Але тоді функція не працюватиме за призначенням.
usr

1

Ви не повинні обидва кидати і ловити один і той же виняток у методі, я навіть думаю, що блок ловлі схопить той самий виняток, який ви кидали, тому ви насправді не кидаєте його.

Якщо це parseIntбуло успішним, то це не є NumberFormatException.

якщо сторона менше нуля, ви повинні кинути a NegativeSideLengthException;

Створіть виняток на замовлення / бізнес NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

Потім sideInputвикидає 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;
}

Ви можете навіть (якщо хочете) додати ще один блок лову для лову, NegativeSideLengthExceptionа не мати спосіб кинути його.

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

Прапори - не гарний спосіб поводження з винятками.


-1

Винятки - це досить кошмарні речі, вони приносять більше складностей, ніж вирішують.

По-перше, якщо ви не виберете свої винятки, абонент може робити лише те on error resume next, що через тиждень навіть ви не знатимете, що може кинути ваша функція та що з нею робити:

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

В основному, якщо ви їх ловите, ви повинні дуже добре розуміти договори та гарантії виключення. Це рідко трапляється в реальному світі. А також ваш код буде важко прочитати.

Крім того, найсмішніше те, що якщо ви справді маєте шанс обробити винятки, вам потрібна справжня мова RAII, яка є якось жартівливою, оскільки Java і .NET - це все про винятки ...

Повторюємо це ще раз, але ...:

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


4
-1 для розміщення прикордонної оренди, наповненої не зовсім коректними - або принаймні висловлюваннями, що залежать від контексту / мови, замість того , щоб відповісти на власне питання ОП.
Петер Тьорьк

@ PéterTörök: "Дайте людині рибу, і ви її годуєте протягом дня. Навчіть чоловіка ловити рибу, а ви годуєте його на все життя". За винятками стоять дуже серйозні проблеми, і люди повинні їх знати -1000 / + 1, мене це зовсім не цікавить.
Кодер

1
Не соромтеся вірити, що ви можете правильно писати правильний код без винятку. Просто не викладайте свої погляди та переконання як факти (навіть якщо Джоель такої самої думки, це все-таки думка, а не факт), і не публікуйте невідповідних відповідей на SE.
Péter Török

@ PéterTörök: Це не думка, це факт, щоразу, коли ви використовуєте компонент, який внутрішньо викидає виняток, вам доведеться сканувати всю ієрархію і перевіряти кожен рядок коду, щоб знати, що виловити, і якщо компоненти пропонують сильні гарантія, і все відкочується назад, або якщо цей вилов - лише помилкове почуття безпеки. Чорт забираєш, ти навіть не знаєш усіх винятків std :: string кидків, ти можеш шукати специфікації, але ніколи не знайдеш. Такі речі, як : throw(thisexception, thatexception)правило, неправильні, і їх ніколи не слід використовувати, оскільки в іншому випадку ви отримаєте несподіваний виняток.
Coder

2
Що ж, як щодо коду, який не використовує винятки? Боже, вам потрібно проскакати код, щоб перевірити кожен рядок, щоб побачити, чи повертаються значення правильно чи ігноруються. І коли ігнороване значення повернення ігнорується, ніхто не помічає, поки, можливо, ваш додаток не вийде з ладу через пару тисяч рядків. Винятки принаймні змушують вас помітити увагу. Так, ви можете проковтнути їх, але тільки з явним catch. Хоча ігнороване повернене значення недоступне - воно може бути ідентифіковане лише шляхом огляду кодового рядка. І останнє, але не менш важливе, у конструкторів немає зворотних значень, це головна причина використання винятків.
Péter Török
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.