Трюк с try ... catch
Запись от Nikto размещена 31.01.2017 в 23:55
Показов 3704
Комментарии 9
|
В данном посте хочу поделиться очень хорошим прогерским лайвхаком. С помощью этого трюка можно внедрить в основной код проверку на правильность ввода, на какие-либо другие пользовательские ошибки и многое другое. Ведь как это иногда бывает: писать основной код очень весело, а вот делать различные проверки на ошибки в ходе выполнения программы очень и очень скучно и не охото. Как только вы прочитаете этот пост всё будет иначе! Нужно знать всего несколько правил: 1) не используйте в основном коде ифы (if) для проверки на ошибки в ходе выполнения; 2) не используйте коды ошибок, вместо них используйте классы ошибок (например в c# собственные классы ошибок наследуйте от Exception, в java от RuntimeException); 3) при проверке на ошибки используйте блоки try ... catch. И это всё, что нужно знать чтобы лвлапнуться в мире кодинга! Завершу примерами кода и примечаниями, которые прояснят всё ситуацию. Проверка заполненности текстовых полей и их валидация (андроид). Смотрите как удобно, у нас нет огромного количества веток из ифов. Если пользователь сделал что-то не так, то мы с помощью throw выбрасываем ошибку и выполнение кода дальше не идёт, а вместо этого в блоке catch пользователю выдаётся сообщение об ошибке, которое зависит от типа исключения.
Если что, то это мой сайт, разница в датах публикации потому, что мне лень всё делать сразу. | ||||||||||
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 9
Комментарии
-
Запись от Avazart размещена 01.02.2017 в 00:15
-
Запись от ProCode размещена 01.02.2017 в 00:32
-
Я обобщил и постарался достаточно просто изложить то, о чём пишет Роберт Мартин в книге "Чистый Код" про try catch.
Сообщение от 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); }
Запись от Nikto размещена 01.02.2017 в 00:46
-
Код будет короче и проще?
Я так не думаю. По крайней мере если писать последовательно а не так как вы переплели код.
Т.е код можно написать лучше чем вы это сделали соответственно о будет выглядеть не хуже чем с исключениями, а то и куда лучше.
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
-
В таком случае программисту не сразу ясно, что someFunction может прерваться. В то время как блоки try catch говорят сами о себе, что try может прерваться в любой момент.
Сообщение от Avazart
К тому же как в юнит тестах вы будете смотреть при каких условиях какая ошибка произошла?
И ещё основная логика программы у вас переплелась с логикой обработки ошибок, а это большой минус.Запись от Nikto размещена 01.02.2017 в 18:35
-
Запись от Avazart размещена 01.02.2017 в 19:43
-
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
-
В принципе концепция верная. Но ответственности я бы разделил посильнее. Это упростит читабельность и как следствие модификацию и поддержку. Наподобие: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 размещена 03.02.2017 в 10:50




