2012-05-09 7 views
6

Прежде всего, я знаю, что стандартный ответ будет заключаться в том, что исключения для управления потоком никогда не используются. Хотя я совершенно согласен с этим, я думал долго о чем-то я иногда делал, что я опишу в следующем псевдокоде:Выбросьте лишнее исключение, чтобы избежать дублирования кода

try 
    string keyboardInput = read() 
    int number = int.parse(keyboardInput) 
    //the conversion succeeds 
    if(number >= 1000) 
     //That's not what I asked for. The message to display to the user 
     //is already in the catch-block below. 
     throw new NumberFormatException() //well, there IS something wrong with the number... 
catch(NumberFormatException ex) //the user entered text 
    print("Please enter a valid number below 1000.") 

Прежде всего, взять этот пример в очень абстрактным способом. Это не обязательно должно произойти. Ситуация просто:

вход

Пользователь должен быть ограничен и может пойти не так в 2 способами, либо на брошенной исключением языка определяет, или чеком. Обе ошибки сообщаются пользователем таким же образом, потому что им не нужно знать техническую разницу в том, что вызвало это.

Я подумал о нескольких способах его решения. Начнем с того, что было бы лучше бросить сделанное на заказ исключение. Проблема, с которой я сталкиваюсь, заключается в том, что если я поймаю ее локально, что делать с другим исключением? Вследствие этого пользовательское исключение было бы причиной второго блока catch, в котором сообщение было бы скопировано так же хорошо. Мое решение:

//number is wrong 
throw new MyException() 
catch(NumberFormatException ex) 
    throw new MyException() 
catch(MyException ex) { 
    print("Please enter...") 

Значение имен исключений - все это здесь. Это приложение специально сделанных исключений является общепринятым, но по существу Я не делал ничего, что отличалось от первого способа: я вынужден зайти в блокировку, хотя и выбрасывая обычное исключение, а не стандартную библиотеку ,

Точно так же применяется для исключения исключения для вызывающего метода (при этом не имеет блока catch для настраиваемого исключения), кажется, имеет больше смысла. Мой метод может пойти не так, как технически двумя способами, но по существу в одну сторону: неправильный ввод пользователя. Поэтому следует написать UserInputException и сделать способ бросить это. Новая проблема: что, если это основной метод приложения?

В настоящее время я не борюсь с конкретным приложением для реализации такого поведения, мой вопрос чисто теоретический и неязычный специфический.

Каков наилучший способ приблизиться к этому?

+0

Я считаю, что несколько языков используют исключения как ядро ​​многих своих структур управления потоком - я думаю, что python по-прежнему использует его для своих итераторных циклов foreach. Поэтому «исключения не используются для контроля потока». кажется немного сильным для меня ... –

ответ

1

Я думаю, что у вас есть по существу несколько способов, чтобы идти об этом с минимальным дублированием кода в виду:

  • Используйте логическую переменную/храните исключение: если в общей логике конкретной задачи вы выполняли ошибку, вы выходите с первого признака ошибки и обрабатываете ее в отдельной обработке ошибок филиал.

    Преимущества: только одно место для обработки ошибки; вы можете использовать любое настраиваемое условие исключения/ошибки, которое вам нравится.

    Недостатки: логика того, что вы пытаетесь достичь, может быть трудно обнаружить.

  • Создайте общую функцию, которую вы можете использовать для информирования пользователя об ошибке (предварительная калькуляция/сохранение всей информации, которая описывает общую ошибку, например сообщение для отображения пользователя), поэтому вы можете просто сделать одну функцию вызов при возникновении ошибки.

    Преимущества: логика вашего намерения может быть понятнее для читателей кода; вы можете использовать anu custom exception/error conditon, который вам нравится.

    Недостатки: ошибка должна обрабатываться в отдельных местах (хотя с предварительно вычисленными/сохраненными значениями не так много копий-пасты, насколько сложно проинформировать пользовательскую часть).

  • Если цель понятна, я не думаю, что исключение исключений из вашего блока try явно является плохой идеей. Если вы не хотите бросать одно из предоставленных системой исключений, вы всегда можете создать свое собственное, которое происходит от одного из них, поэтому вам нужно только минимальное количество (предпочтительно одно) блоков catch.

    Преимущества: только одно место для обработки условия ошибки - если в try-блоке есть только один тип исключения.

    Недостатки: если более чем один тип исключения брошен, вам нужны вложенные блоки примерки уловов (для распространения исключения наиболее наружу один) или очень общего назначение (например, Exception) поймать блок, чтобы избежать необходимости дублирование отчетов об ошибках.

  • +0

    Очень хорошо структурированный и ясный ответ, я очень ценю, что вы перечисляли некоторые возможности. – MarioDS

    +0

    Вы получаете щедрость, потому что все остальные закодировали либо одну из трех вещей, которые вы сказали, либо ответ действительно не соответствовал моим потребностям. Я думаю, что это что-то зависящее от ситуации. Благодарю. – MarioDS

    1

    Вам не нужны никакие исключения в этом конкретном примере.

    int number; 
    if (int.TryParse(keyboardInput, out number) && number < 1000) // success 
    else // error 
    

    Однако ситуация вы охарактеризовали распространена в бизнес-программ, и бросать исключение, чтобы достичь равномерного обработчика является довольно распространенным явлением.

    Одним из таких шаблонов является проверка XML, за которой следует XSLT. В некоторых системах недопустимый XML обрабатывается путем исключения исключений проверки.В этих системах, это довольно естественно использовать уже существующие обработки исключений в XSLT (который, естественно, может обнаружить некоторые классы ошибок данных, что конкретный язык проверки не может):

    <xsl:if test="@required = 'yes' and @prohibited = 'yes'> 
        <xsl:message terminate='yes'>Error message</xsl:message> 
    </xsl:if> 
    

    Важно, чтобы видеть, что если такие условия (ожидается, что это произойдет только во время раннего тестирования интеграции и исчезнет, ​​поскольку дефекты в других модулях будут исправлены), most of the typical concerns вокруг исключения исключений для управления потоком на самом деле не применяются.

    +1

    Я считаю, что вопрос был более общим, чем это, речь идет не о преобразовании строки в int, а в целом, когда некоторые тесты вызывают исключение, а другие нет. Обратите внимание, что код псевдокод, а не какой-то определенный язык (хотя он очень похож на Java). – alexg

    +0

    @alexg Я признаю это, но это потому, что мои основные языки - Java и C#. – MarioDS

    +0

    Я также подумал, что это был вопрос Java. Но потом я заметил теги: дизайн, дублирование кода, управление потоком. – alexg

    1

    Как я вижу это:

    Предполагая, что нет никакого другого способа разобрать ИНТ, что не бросает исключение, ваш код, как сейчас, правильно и элегантно.

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

    error=false; 
    
    try { 
        string keyboardInput = read(); 
        int number = int.parse(keyboardInput); 
        //the conversion succeeds 
        if(number >= 1000) { 
         //That's not what I asked for. The message to display to the user 
         //is already in the catch-block below. 
         error=true; 
    } catch(NumberFormatException ex) { //the user entered text 
        error=true; 
    } 
    
    if (error) 
        print("Please enter a valid number below 1000."); 
    

    Также вы можете подумать о том, почему вы пытаетесь объединить две ошибки в одну. Вместо этого вы могли бы сообщить пользователю, какие ошибки они сделали, что может быть более полезным в некоторых случаях:

    try { 
        string keyboardInput = read(); 
        int number = int.parse(keyboardInput); 
        //the conversion succeeds 
        if(number >= 1000) { 
         //That's not what I asked for. The message to display to the user 
         //is already in the catch-block below. 
         print("Please enter a number below 1000."); 
    
    } catch(NumberFormatException ex) { //the user entered text 
        print("Please enter a valid number."); 
    } 
    
    +0

    Я уже думал об использовании логической переменной и использовании блока 'finally', это действительно кажется хорошей идеей. Вторая часть кода - именно то, чего я пытаюсь избежать. Пользователь понимает «действительное число ниже 1000», что он не должен вводить текст, а не номер больше 1000. Вся причина в том, что для того, чтобы получать прилично-форматированные MessageBoxes в ** много ** языках, это довольно большой кусок кода. – MarioDS

    +0

    Я понимаю, перед тем, как я столкнулся с той же самой диллемой. Использование блока 'finally' - хорошая идея. В основном вам придется торговать одним преимуществом для другого. Является ли ваша главная забота о производительности или удобство/короткое понимание кода? Использование логического значения делает цель не дублировать код ящика сообщений. – alexg

    1

    Что касается приближения к этой проблеме проверки, написав несколько классов валидатора, которые принимают ошибки ввода и возврата, или нет ошибок. Что касается вашей борьбы с исключениями: поместите эту логику в каждый валидатор и разобраться с ним там в каждом конкретном случае.

    после этого вы определяете правильные валидаторы для использования для ввода, собираете их ошибки и обрабатываете их.

    преимущества этого являются:

    1. Validators сделать одну вещь, проверки одного случая
    2. его до функции проверки, чтобы решить, как обрабатывать ошибки. Вы нарушаете первую ошибку проверки или собираете их все, а затем занимаетесь ими?
    3. Вы можете написать свой код таким образом, что основная функция проверки может проверять различные типы ввода с использованием того же кода, просто выбирая правильные валидаторы, используя ваш любимый метод.

    и недостатки:

    1. Вы закончите писать больше коды (но если вы используете Java, то это должно быть введено в ведро пособий ")

    вот некоторые пример псевдокода:

    validate(input): 
        validators = Validator.for(input.type) 
        errors = [] 
        for validator in validators: 
         errors.push(validator.validate(input)) 
    
        if errors: 
         throw PoopException   
    

    и некоторые валидаторы:

    MaxValidator extends IntValidator: 
        validate(input): 
         errors = [] 
         errors.push(super.validate(input)) 
         if input > 1000: 
          errors.push("bleee!!!! to big!") 
         return errors 
    
    IntValidator: 
        validate(input): 
         try: 
          int.parse(input) 
         catch NumberFormatException: 
          return ['not an int'] 
         return [] 
    

    Конечно, вам нужно будет сделать некоторые обманы, чтобы родительский валидатор, возможно, вернул вам действительную версию ввода, в этом случае строка «123» преобразуется в int, поэтому макс. Валидатор может ее обрабатывать, но это может быть легко достигнуто, сделав валидаторы statefull или какой-либо другой магии.

    +0

    Мне нравится ваш подход, особенно потому, что он больше ООП. Похоже, вы закончите с более структурированной программой. Однако мой вопрос не всегда касается проверки ввода (я знаю, что это не ясно, но мне пришлось использовать ** некоторый пример **). Я не понимаю, что вы имеете в виду, написав больше кода в java - это преимущество? – MarioDS

    +0

    ** написать больше кода в java - хорошая вещь ** была шуткой.Java воспринимается как очень многословный язык, и я намекал на это;) - но да, это подход ООП, который будет намного проще реорганизовать в будущем, и я считаю, что думать о проблемах в небольших кусках также полезно в много способов – mkoryak

    5

    Я бы назвал первое исключение низкоуровневым, и я бы обработал его (путем перевода в этом случае) в точке вызова. Я нахожу, что это приводит к коду, который легче поддерживать и реорганизовывать позже, поскольку у вас меньше типов исключений для обработки.

    try 
        string keyboardInput = read() 
        try 
        int number = int.parse(keyboardInput) 
        catch(NumberFormatException ex) 
        throw MyException("Input value was not a number") 
    
        //the conversion succeeds 
        if(number >= 1000) 
        throw MyException("Input value was out of range") 
    
    catch(MyException ex) //the user entered text 
        print(ex.ToString()) 
        print("Please enter a valid number below 1000.") 
    
    1

    Я не могу видеть этот ответ в любом месте здесь, поэтому я просто отправлю его в качестве другой точки зрения.

    Как мы все знаем, вы можете отказаться от правил, если вы знаете их достаточно хорошо, поэтому вы можете использовать бросок исключения для управления потоком, если знаете, что это лучшее решение для вашей ситуации. Из того, что я видел, это обычно происходит с некоторыми немыми фреймворками ...

    Тем не менее, перед тем Java 7 (который принес нам могучий multicatchconstruct), это был мой подход, чтобы избежать повторения кода:

    try { 
        someOffendingMethod(); 
    } catch (Exception e) { 
        if (e instanceof NumberFormatException || e instanceof MyException) { 
         System.out.println("Please enter a valid number."); 
        } 
    } 
    

    Это действует метод в C# тоже.

    +0

    Довольно умный взлом до многоячеистого, но не 'instanceof'a довольно требовательная операция? Кроме того, этот пример кода не совсем то же самое, что я делаю. Я намеренно «руляю» программу на ветку catch, потому что она просто содержит код сообщения, подходящий для ситуации. – MarioDS

    +0

    Когда вы используете 'Исключения' для управления потоком вашей программы, вы абсолютно не должны беспокоиться о том, что' instanceof' является медленным :). Но да, подход, который я опубликовал, - довольно хороший способ практически написать вашу конструкцию, а не теоретическую дискуссию об этом. Я просто признал, что иногда это необходимо сделать, так почему бы не сделать это наилучшим образом? –

    Смежные вопросы