Pull to refresh

Исключение — твой друг

Development for Android *
image
В середине девяностых, когда я переходил с программирования под DOS на Windows, мой наставник познакомил меня с механизмом исключений.
С тех пор в моём сознании укоренилось мнение: программа, падающая с исключением — плохая программа. Все исключения надо обрабатывать и завершать работу приложения в случае нештатной ситуации самостоятельно.
И это вполне актуально для обычного приложения под Windows. Ведь в случае падения приложения пользователь получает невнятное сообщение об ошибке и, как результат, негативное восприятие нестабильно работающего приложения.
Моё мнение начало меняться после знакомства с инструментами автоматической обработки исключений (таких как EurekaLog и аналогов).
И окончательно поменялось после знакомства с системой отчетов Google Play.
Этот пост — крик души против тех тысяч примеров, которые учат нас пихать в свой код необдуманные проверки.

Поводом для написания данного поста стал диалог с товарищем, который просил совета по реализации взаимодействия NDK с Java-кодом. Мне были показаны исходные коды, написанные на основе этого мануала.
Код, вызывающий вопросы:
    jmethodID method = env->GetStaticMethodID(interfaceClass, "callBack", "(Ljava/lang/String;)V");
    if(!method) {
        LOGE("callback_handler: failed to get method ID");
        return;
    }
    env->CallStaticVoidMethod(interfaceClass, method, js);

Это классический пример кода, который выглядит правильно, но на самом деле только создает новые проблемы.
Проверка здесь нужна только в одном случае — если метод callBack не обязан существовать и может отсутствовать в штатном режиме приложения.
Проверки нужны когда мы проверяем результат работы сетевых модулей или когда мы работаем с внешними файлами и т.п.
То есть, когда ошибка может возникать по вине внешних факторов. В этом случае проверка на ошибки — наша прямая обязанность.
Ситуация полностью меняется, когда ошибка генерируется нашим кодом и не зависит от внешних факторов.
Если callBack описан нами и должен присутствовать в коде, проверка не нужна.

Проверка лишь замаскирует ошибку.

Очень важно понимать, что маскировка ошибок — это плохо. Гораздо хуже, чем падение приложения с исключением.
Вы можете сказать, что сообщение выводится в лог и всплывает во время тестирования.
Ну, отлично, если всплывает. А если не всплывёт? Если пользователю уйдёт неработающий код, который будет просто молча игнорировать вызов callBack?
Ваши пользователи будут работать с программой, которая не выполняет задуманный функционал. Хорошо, если это какая-нибудь мелочь, не влияющая серьёзно на работу приложения. Но что, если там скрывается важная часть кода, из-за которого все результаты работы пользователя оказываются некорректными?
Уберите проверку и первый пользователь, столкнувшийся с вызовом callBack, получит падение приложение. А Вы — отчёт об ошибке с заполненным call stack. Как минимум, это обратит Ваше внимание на проблему. Да, пользователь будет недоволен. Но Вы уже через пять часов зальёте на Google Play новую версию и тысячи пользователей даже не узнают об этой проблеме.
Это гораздо лучше, чем огромная клиентская база, работающая с приложением с миной замедленного действия.

Проверки на ошибки — это важная часть работы приложения. Ошибки самого разного рода возникают постоянно. Не бывает ни одного запуска сколь-либо сложного приложения, который бы произошел без возникновения нескольких ошибок. Но надо отделять штатные ошибки от нештатных. Штатные ошибки — те ошибки, которые допустимы и влияение которых предсказуемо. Эти ошибки нужно обрабатывать внутри приложения и менять логику работы в зависимости от ситуации. Нештатные ошибки ни в коем случае нельзя обрабатывать стандартными проверками. Нельзя просто взять и засунуть блок кода в if c проверкой на NULL. Вы всегда должны чётко понимать, почему объект стал NULL и чем это грозит в работе вашего приложения.

Так что же делать с проверками?

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

Конечно, я не открыл Америку для профессионалов, которые на собственном опыте знают, насколько вредна маскировка ошибок.
Этот текст нацелен на новичков, которые изучают программирование по примерам из интернета… А примеры, тем временем, пестрят бессмысленными и беспощадными проверками…
Tags:
Hubs:
Total votes 33: ↑27 and ↓6 +21
Views 18K
Comments Comments 25