Ошибок в коде из за которых результаты выполнения программы были неправильными

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

Можно ждать пока ошибка возникнет и производить отладку шаг за шагом чтобы найти её, а можно предусмотреть возникновение ошибки заранее и быть наготове.

Вообще, ошибки можно разделить на два типа:

  1. Ошибки во внешних данных
  2. Ошибки в логике приложения
  • 1 Ошибки во внешних данных
  • 1.1 Исключения
  • 2.1 Утверждения

Ошибки во внешних данных

К первому типу относятся в том числе ошибки:

  1. В данных введённых пользователем, считанных из файла или из консоли;
  2. Ошибки связанные с отсутствием файлов, невозможности их прочитать или записать;
  3. “Битые данные” принятые по сети;
  4. Ошибки, поизошедшие во внешних устройствах;
  5. В данных, принятых из других модулей большого проекта.

Чаще всего, ошибки этого типа мы можем предугадать, отловить и обработать.

Задача с собеседования в Google и Apple

Один из вариантов обработки таких ошибок заключается в создании исключений. (об этом будет сказано ниже)

Приложение при этом может поступить одним (или несколькими) из приведённых ниже способов:

  • Мягко завершиться (без потери данных и непонятных пользователю сообщений);
  • Оповестить пользователя о возникновении ошибки (внятным сообщением, позволяющим понять, что нужно делать);
  • Заменить некорректные данные приближёнными к истине или просто корректными значениями;
  • Повторить попытку получить данные;
  • Пропустить выполнение методов, зависящих от некорректных данных.

Исключения

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

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

class MinusDeltaException < >;
int CrashingMethod ( int lastTime , int delta ) throw ( MinusDeltaException )
if ( delta < 0 ) throw MinusDeltaException ( ) ;
return lastTime + delta ;

Обработка ошибок: Защитное программирование

В любой программе, сложнее чем «Hello, world» , неизбежно будут происходить ошибки и различные сбои. Поэтому если вы хотите писать надежный и стабильный код, то обязаны заботиться обо всем этом. Конечно, в большинстве случаев нет смысла становиться параноиком и проверять абсолютно все. С другой стороны, во многом это зависит от области применения приложения, которое вы разрабатываете.

Читайте также:
Как сбросить программу для открытия файла

Если это система контроля банковских переводов или управления полетом спутника, то единственная ошибка в коде может обойтись очень дорого. В этом случае вам на помощь приходит «защитное программирование». Одно из лучших описаний этой методики, на мой взгляд, приводится в следующих книгах:

РЕАКЦИЯ НА НЕДОПУСК К ОГЭ 2021

  1. Ремесло программиста. Практика написания хорошего кода;
  2. Совершенный код.

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

Примеры кода я буду приводить на C++, однако это не должно стать для вас серьезной помехой, если вы пишите на Java, C# или каком-нибудь другом языке со схожим синтаксисом, поскольку многие техники обработки ошибок являются универсальными.

Коды ошибок

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

Двойная ответственность

Вот как может выглядеть подобная функция:

int registerUser( const User // … >

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

User user; // Заполняем структуру user int userID = registerUser( user ); if( userID == -1 ) < // Регистрация не удалась! >

Такой способ применяется довольно часто, но он имеет некоторые очевидные недостатки. Например, когда мы инициализируем переменную userID значением, которое возвращает функция registerUser() , то на самом деле мы врем.

Ведь эта переменная хранит идентификатор пользователя ИЛИ код ошибки, то есть должна называться userIDOrErrorCode . Имя получилось длинным и пользоваться им будет неудобно. Но сразу становится понятно, что эта переменная имеет два назначения (выглядит подозрительно, не правда ли?). Однако даже это не является главным недостатком. В какой-то момент требования к типу идентификатора могут измениться. Например, мы можем решить, что лучше использовать беззнаковое целое число:

typedef unsigned int UserID; UserID registerUser( const User

Читайте также:
Что такое соц программы

Обратите внимание, что в первую очередь я добавил определение typedef для типа UserID . Теперь мы сможем менять фактический тип идентификатора всего в одной строке. Для сравнения, когда мы явно пользовались типом int , то в зависимости от объема кода подобная тривиальная операция могла бы занять несколько часов. Поэтому старайтесь не повторяться (см. Принцип DRY в действии).

Кроме того, теперь мы явно указываем, что функция registerUser() возвращает именно идентификатор UserID , а не идентификатор ИЛИ код ошибки. Иначе мы вновь обманываем тех, кто будет вызывать нашу функцию.

Указатель для ошибки

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

UserID registerUser( const User

Этой функцией мы можем пользоваться следующим образом:

User user; // Заполняем структуру user bool ok = false; UserID userID = registerUser( user, if( !ok ) < // Регистрация не удалась! >

Выглядит неплохо. Но у вас мог возникнуть вопрос: «А какое значение будет у переменной userID , если ok == false «? Можно предположить, что это будет 0 , то есть функция registerUser() должна вернуть некое нейтральное значение. Например, в случае, когда ожидается возврат класса или структуры, это может быть Null -объект или объект с функцией-членом на подобии isValid() , которая возвращает false , если экземпляр класса находится в некорректном состоянии. Кстати, если бы registerUser() была не обычной функцией, а являлась членом класса, то мы бы могли вообще не возвращать никаких признаков ошибки, но добавить в сам класс что-то вроде getLastError() .

Вот это поворот

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

bool registerUser( const User

C#: обработка ошибок

Speak.Me Учить иностранные слова

Исключения, их обработка, и некоторые другие моменты, связанные с ошибками в приложении на C#.

Исключения (Exceptions) и инструкция try

Инструкция try отмечает блок кода как объект для обработки ошибок или очистки. После блока try обязательно должен идти либо блок catch , либо блок finally , либо они оба. Блок catch выполняется, когда внутри блока try возникает ошибка. Блок finally выполняется после того, как прекращает выполнять блок try (или, если присутствует, блок catch ), независимо от того, выполнился ли он до конца или был прерван ошибкой, что позволяет выполнить так называемый код очистки.

Блок catch имеет доступ к объекту исключения ( Exception ), который содержит информацию об ошибке. Блок catch позволяет обработать исключительную ситуацию и как-либо скорректировать ошибку или выбросить новое исключение. Повторное выбрасывание исключения в блоке catch обычно применяется с целью логирования ошибок или чтобы выбросить новое, более специфическое исключение.

Читайте также:
Как обновить программу вайбер

Блок finally добавляет в программу прогнозируемость, позволяя выполнить определенный код при любых обстоятельствах. Это может быть полезно для выполнения операций очистки, например, закрытия сетевого подключения и т.д.

В целом конструкция try выглядит следующим образом:

. . . // в пределах этого блока может быть выброшено исключение
catch ( ExceptionA ex )
. . . // обработчик исключений типа ExceptionA
catch ( ExceptionB ex )
. . . // обработчик исключений типа ExceptionB
. . . // код очистки

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

int x = 3 , y = 0 ;
Console . WriteLine ( x / y ) ;

Чтобы этого избежать можно использовать конструкцию try :

int x = 3 , y = 0 ;
Console . WriteLine ( x / y ) ;
catch ( DivideByZeroException ex )
Console . Write ( «y cannot be zero. » ) ;
// выполнение программы продолжится отсюда

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

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

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

Оговорка catch

В оговорке catch указывается какой тип исключения она должна перехватывать. Это может быть либо System.Exception , либо его производный класс. Перехватывая непосредственно System.Exception , мы перехватим все возможные ошибки. Это может быть полезно в нескольких случаях:

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

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

Можно обработать несколько типов исключений с помощью нескольких оговорок catch:

Источник: devkazakov.com

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru