Форум программистов, компьютерный форум, киберфорум
Наши страницы
Nikto
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Трюк с try ... catch

Запись от Nikto размещена 31.01.2017 в 23:55
Обновил(-а) Nikto 08.06.2017 в 10:35

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

Нужно знать всего несколько правил:
1) не используйте в основном коде ифы (if) для проверки на ошибки в ходе выполнения;
2) не используйте коды ошибок, вместо них используйте классы ошибок (например в c# собственные классы ошибок наследуйте от Exception, в java от RuntimeException);
3) при проверке на ошибки используйте блоки try ... catch.

И это всё, что нужно знать чтобы лвлапнуться в мире кодинга!

Завершу примерами кода и примечаниями, которые прояснят всё ситуацию.
Проверка заполненности текстовых полей и их валидация (андроид). Смотрите как удобно, у нас нет огромного количества веток из ифов. Если пользователь сделал что-то не так, то мы с помощью throw выбрасываем ошибку и выполнение кода дальше не идёт, а вместо этого в блоке catch пользователю выдаётся сообщение об ошибке, которое зависит от типа исключения.
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
try
{
       String email = ((EditText)findViewById(R.id.login_edittext_email)).getText().toString();
       String pass = ((EditText)findViewById(R.id.login_edittext_pass)).getText().toString();
 
       if ((email.isEmpty()) || (pass.isEmpty()))
       {
                throw new EmptyEditTextException();
       }
       else if (!Utils.isValidEmail(email))
       {
                throw new NotValidEmailException();
       }
 
       ...//создаём параметры для входа
       account.login(params);
}
catch (NotValidEmailException e)
{
       Toast.makeText(getApplicationContext(), "Некорреткный email адрес", Toast.LENGTH_SHORT).show();
}
catch (EmptyEditTextException e)
{
       Toast.makeText(getApplicationContext(), "Не введён логин или пароль", Toast.LENGTH_SHORT).show();
}
Или вот ещё пример на c#. Проверяем сначала всё ли готово для печати, если нет, то показываем ошибку, если да, то начинаем печать. Опять же код мог бы быть куда более раздутым без try ... catch. Обратите внимание, что блоки try ... catch легко можно добавить в уже готовый код. Т.е. сначала можно реализовать основную логику программы, а потом спокойно добавить обработку ошибок.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try
{
       checkExistAdobeReaderPath(); //проверяем указан ли путь до Adobe Reader
       checkOpenedPdfFile(); //проверяем выбран ли PDF на печать
       /*если же какое либо условие не выполнилось то эти функции выкинут exception, а в catch мы их отловим)*/
       printDocument(); //отправляем на печать
}
catch (PdfFileNotOpenedException)
{
       MessageBox.Show("Pdf файл не выбран", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (AdobeReaderPathNullException)
{
       MessageBox.Show("Не указан путь к AdobeReader", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Источник: https://catplusplus.ru/blog/trick_with_try_catch
Если что, то это мой сайт, разница в датах публикации потому, что мне лень всё делать сразу.
Размещено в Без категории
Просмотров 747 Комментарии 9
Всего комментариев 9
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    Цитата:
    Нужно знать всего несколько правил:
    1) не используйте в основном коде ифы (if) для проверки на ошибки в ходе выполнения;
    2) не используйте коды ошибок, вместо них используйте классы ошибок (например в c# собственные классы ошибок наследуйте от Exception, в java от RuntimeException);
    3) при проверке на ошибки используйте блоки try ... catch.
    Мне как человеку который пишет кода в основном на С++ интересно, откуда вылезли эти правила?
    Т.е в чем польза/суть?

    Цитата:
    вместо них используйте классы ошибок (например в c# собственные классы ошибок наследуйте от Exception, в java от RuntimeException);
    И откуда новый термин "классы ошибок" ....
    Запись от Avazart размещена 01.02.2017 в 00:15 Avazart вне форума
    Обновил(-а) Avazart 01.02.2017 в 00:17
  2. Старый комментарий
    Аватар для ProCode
    Пишу на php но трай-кэтч действительно использую оч редко. Разве что для соединения с БД.

    Присмотрюсь внимательней к этой конструкции. Спасиб
    Запись от ProCode размещена 01.02.2017 в 00:32 ProCode вне форума
  3. Старый комментарий
    Аватар для Nikto
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Мне как человеку который пишет кода в основном на С++ интересно, откуда вылезли эти правила?
    Т.е в чем польза/суть?


    И откуда новый термин "классы ошибок" ....
    Я обобщил и постарался достаточно просто изложить то, о чём пишет Роберт Мартин в книге "Чистый Код" про try catch.

    Польза в том, что код перестаёт вевтиться, и увеличивается наглядность кода. Представьте если мы не будем использовать try catch, а вместо них просто ифы. Получится нечто вроде:
    Java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    boolean isErrors = false;
    String email = ((EditText)findViewById(R.id.login_edittext_email)).getText().toString();
    String pass = ((EditText)findViewById(R.id.login_edittext_pass)).getText().toString();
     
    if ((email.isEmpty()) || (pass.isEmpty()))
    {
        Toast.makeText(getApplicationContext(), "Не введён логин или пароль", Toast.LENGTH_SHORT).show();
        isErrors = true;
    }
    else if (!Utils.isValidEmail(email))
    {
        Toast.makeText(getApplicationContext(), "Некорреткный email адрес", Toast.LENGTH_SHORT).show();
        isErrors = true;
    }
     
    if (!isErrors)
    {
        ...//создаём параметры для входа
        account.login(params);
    }
    В таком случае код стал менее читаемым. Обработка ошибок не путается с основным кодом. Я считаю, что код с try catch выглядит гораздо лучше, легче пишется и этим гораздо эффективней.
    Запись от Nikto размещена 01.02.2017 в 00:46 Nikto на форуме
    Обновил(-а) Nikto 01.02.2017 в 00:47
  4. Старый комментарий
    Аватар для Avazart
    Цитата:
    Представьте если мы не будем использовать try catch,
    Код будет короче и проще?

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

    C#
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    bool сheckField(EditText text,String msg)
    {
      if(email.isEmpty())
      {
         Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
         return false;
      } 
      return true;
    }
     
     
    void someFunction()
    {
     
        String email = ((EditText)findViewById(R.id.login_edittext_email)).getText().toString();
        if(!сheckField(email,"Не введён логин"))
           return;
     
     
        String pass = ((EditText)findViewById(R.id.login_edittext_pass)).getText().toString();
        if(!сheckField(password,"Не введён пароль"))
             return;
    // ...  
    }
    Запись от Avazart размещена 01.02.2017 в 13:40 Avazart вне форума
    Обновил(-а) Avazart 01.02.2017 в 15:34
  5. Старый комментарий
    Аватар для Nikto
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    C#
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    bool сheckField(EditText text,String msg)
    {
      if(email.isEmpty())
      {
         Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
         return false;
      } 
      return true;
    }
     
     
    void someFunction()
    {
     
        String email = ((EditText)findViewById(R.id.login_edittext_email)).getText().toString();
        if(!сheckField(email,"Не введён логин"))
           return;
     
     
        String pass = ((EditText)findViewById(R.id.login_edittext_pass)).getText().toString();
        if(!сheckField(password,"Не введён пароль"))
             return;
    // ...  
    }
    В таком случае программисту не сразу ясно, что someFunction может прерваться. В то время как блоки try catch говорят сами о себе, что try может прерваться в любой момент.
    К тому же как в юнит тестах вы будете смотреть при каких условиях какая ошибка произошла?
    И ещё основная логика программы у вас переплелась с логикой обработки ошибок, а это большой минус.
    Запись от Nikto размещена 01.02.2017 в 18:35 Nikto на форуме
  6. Старый комментарий
    Аватар для Avazart
    Цитата:
    В таком случае программисту не сразу ясно, что someFunction может прерваться.
    Она не может прерваться.

    Цитата:
    И ещё основная логика программы у вас переплелась с логикой обработки ошибок, а это большой минус.
    Я не понял утверждения.Говорите конкретнее. Где тут "логика программы" и где обработка ошибок. Как помне тут весь код обработка ошибок.
    Или точнее сказать валидация данных из GUI.

    Вы не владеете терминами и пытаетесь еще кого-то учить.

    Цитата:
    К тому же как в юнит тестах вы будете смотреть при каких условиях какая ошибка произошла?
    Чепуха.... Юнит тестах для GUI?
    Запись от Avazart размещена 01.02.2017 в 19:43 Avazart вне форума
    Обновил(-а) Avazart 01.02.2017 в 19:52
  7. Старый комментарий
    Аватар для Avazart
    1. Стоит выбирать что использовать коды возврата или исключения в зависимости от ситуации от того что более уместно, в разных ситуация есть свои +/- в использовании того или иного подхода. В большинстве в случае выбирать ибо подход диктуется используемыми фреймворками, библиотеками.

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

    Что если полей будет не 2 а 20 ? Каким будет код ? Он будет из соплей try... catch Ex1 ... catch Ex2 ... catch Ex20
    Логично что если возможно стоило свести все к одному классу исключений и обрбатывать их одинаковым образом, а не так как у вас вкоде.

    C#
    1
    2
    3
    4
    5
    6
    7
    8
    
    catch (PdfFileNotOpenedException)
    {
           MessageBox.Show("Pdf файл не выбран", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    catch (AdobeReaderPathNullException)
    {
           MessageBox.Show("Не указан путь к AdobeReader", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    Явное дублирование кода, а можно было обойтись одним классом исключения.
    C#
    1
    2
    3
    4
    5
    6
    7
    8
    
          if ((email.isEmpty()) || (pass.isEmpty()))
           {
                    throw new FieldNotValidlException("Pdf файл не выбран");
           }
           else if (!Utils.isValidEmail(email))
           {
                    throw new FieldNotValidlException("Не указан путь к AdobeReader");
           }
    C#
    1
    2
    3
    4
    5
    6
    
    //...
    catch (FieldNotValidlException e)
    {
           MessageBox.Show(e.ErrorMessage, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    ]
    Как по мне порождать еще два лишних класса исключений(которые кстати никак не показаны в коде), что бы обработать "пустые" поля это слишком.
    Понятно что это лишь пример, но на нем видно что это излишне.
    Я понимаю использование своих исключений, когда пишеш свою бибилотеку классов.

    2. Стоит четко представлять с какой целью и какие случаи вы обрабатываете (или не обрабатываете) в каких ситуациях.

    Цитата:
    а вот делать различные проверки на ошибки в ходе выполнения программы очень и очень скучно и не охото.
    Не стоит пытаться обработать все и вся.

    Например можно:
    1. Обрабатывать исключение с цель его подавления (заглушка.)
    2. С целью перевода ошибки с интернацианализации или выдачи более понятной ошибки пользователю.
    3. С цель преоразования и "перекидывания" исключения на более высокий уровень...
    итд...

    3. Не стоит путать ошибки с исключениями. Исключения более широкое понятие.
    Запись от Avazart размещена 01.02.2017 в 20:02 Avazart вне форума
    Обновил(-а) Avazart 01.02.2017 в 20:28
  8. Старый комментарий
    Аватар для HighPredator
    В принципе концепция верная. Но ответственности я бы разделил посильнее. Это упростит читабельность и как следствие модификацию и поддержку. Наподобие:
    Java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    {
            LoginParameters parameters;
     
            try {
                parameters = LoginParameters.newBuilder()
                        .withEmail(getTextContentFromEditById(R.id.login_edittext_email))
                        .withLogin(getTextContentFromEditById(R.id.login_edittext_login))
                        .withPassword(getTextContentFromEditById(R.id.login_edittext_password))
                        .build();
     
                account.login(parameters);
            } catch (UserInterfaceException e) {
                LOG.error(e, e);
            } catch (RequiredParameterMissingException e) {
                // handle exception
            }
        }
     
        public static String getTextContentFromEditById(final int id) throws UserInterfaceException {
            String content = null;
     
            EditText editText = (EditText) findViewById(id);
     
            if (editText != null) {
                Editable editable = editText.getText();
     
                if (editable != null) {
                    content = editText.toString();
                } else {
                    throw new UserInterfaceError("Editable for view is misconfigured");
                }
            } else
                throw new UserInterfaceException("View not found with id = " + id);
     
            return content;
        }
    }
    Java
    1
    2
    3
    4
    5
    
    public class UserInterfaceException extends Exception {
        public UserInterfaceException(String message) {
            super(message);
        }
    }
    Java
    1
    2
    3
    4
    5
    
    public class UserInterfaceError extends Error {
        public UserInterfaceError(String message) {
            super(message);
        }
    }
    Java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public class RequiredParameterMissingException extends Exception {
        public RequiredParameterMissingException(String message) {
            super(message);
        }
     
        public RequiredParameterMissingException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    Java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    
    public class LoginParameters {
        private String email;
        private String login;
        private String password;
     
        private LoginParameters(String email, String login, String password) {
            this.email = email;
            this.login = login;
            this.password = password;
        }
     
        public static Builder newBuilder() {
            return new Builder();
        }
     
        public String getEmail() {
            return email;
        }
     
        public String getLogin() {
            return login;
        }
     
        public String getPassword() {
            return password;
        }
     
        public static final class Builder {
            private Optional<String> email;
            private Optional<String> login;
            private Optional<String> password;
     
            private Builder() {
                this.email = Optional.absent();
                this.login = Optional.absent();
                this.password = Optional.absent();
            }
     
            private boolean isFieldValid(final String field) {
                return true;
            }
     
            private void validatePassword() throws RequiredParameterMissingException {
                if (!password.isPresent())
                    throw new RequiredParameterMissingException("Password is missing");
                else if (!isFieldValid(password.get()))
                    throw new RequiredParameterMissingException("Invalid password");
            }
     
            private void validateLogin() throws RequiredParameterMissingException {
                if (!login.isPresent())
                    throw new RequiredParameterMissingException("Login is missing");
                else if (!isFieldValid(login.get()))
                    throw new RequiredParameterMissingException("Invalid login");
            }
     
            private void validateEmail() throws RequiredParameterMissingException {
                if (!email.isPresent())
                    throw new RequiredParameterMissingException("E-mail is missing");
                else if (!isFieldValid(email.get()))
                    throw new RequiredParameterMissingException("Invalid e-mail");
            }
     
            public Builder withEmail(final String email) {
                this.email = Optional.fromNullable(email);
                return this;
            }
     
            public Builder withLogin(final String login) {
                this.login = Optional.fromNullable(login);
                return this;
            }
     
            public Builder withPassword(final String password) {
                this.password = Optional.fromNullable(password);
                return this;
            }
     
            public LoginParameters build() throws RequiredParameterMissingException {
                validateEmail();
     
                validateLogin();
     
                validatePassword();
     
                return new LoginParameters(email.get(), login.get(), password.get());
            }
        }
    }
    Сделал схематично, но приведенное легко масштабируется на произвольное число исключений по части вадидации.
    Запись от HighPredator размещена 03.02.2017 в 10:34 HighPredator вне форума
  9. Старый комментарий
    Аватар для HighPredator
    И рантайм исключения здесь не надо использовать. Максимум в моем примере эррор на него заменить.
    Запись от HighPredator размещена 03.02.2017 в 10:50 HighPredator вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru