В этом посте я поделюсь с вами, как делается обработка ошибок и какие у нас есть варианты. Обработка ошибок — сложная тема 🙂
Я добавлю некоторый контекст из Википедии о том, что такое обработка исключений, прежде чем идти по кроличьей норе обработки исключений
Обработка исключений — это процесс реагирования на возникновение во время вычислений исключений — аномальных или исключительных условий, требующих специальной обработки, — часто нарушающих нормальный ход выполнения программы . Это обеспечивается специализированными конструкциями языка программирования , механизмами компьютерного оборудования , такими как прерывания или
средства IPC операционной системы, такие как сигналы .
Как правило, исключение нарушает нормальный поток выполнения и выполняет предварительно зарегистрированный обработчик исключений . Детали того, как это делается, зависят от того, является ли это аппаратным или программным исключением, и как реализовано программное исключение. Некоторые исключения, особенно аппаратные, могут обрабатываться настолько изящно, что выполнение может возобновиться с того места, где оно было прервано.
Источник — https://en.wikipedia.org/wiki/Exception_handling
Мало что выделяется: « нарушает нормальный ход выполнения программы », « предварительно зарегистрированный обработчик исключений», аппаратное или программное исключение.
Это объясняет, что такое обработка ошибок, поэтому я не буду тратить на это время.
Следует упомянуть одну интересную вещь: существуют два типа исключений (аппаратное и программное обеспечение), друзья по аппаратному обеспечению справились с этим очень изящно, и разработчик программного обеспечения должен был это сделать.
Программное обеспечение — это сложно, а слишком много языков программирования делает его еще сложнее.
Я хочу, чтобы вы обратились к сообщению о простом тестировании, которое может предотвратить большинство, в котором я пытаюсь объяснить побочный эффект неправильной обработки ошибок, и это чисто как результат обработки исключений.
C путь
Я уверен, что вы видели это и думали, что «это правильный путь?» ,
Фрагмент кода обработки ошибок C
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
int main () { FILE * fp; fp = fopen ( "somejunkfile.txt" , "rb" ); if (fp == NULL) { printf( "errno: %d76485//" ////, errno); printf( "Error opening the file: %s76485//" ////, strerror//(errno)); exit(EXIT_FAILURE) } else { fclose (fp); } return 0 ; } |
Этот подход имеет несколько проблем
— Вы должны проверить на наличие ошибок после вызова каждой функции, которая может дать сбой.
— Отсутствие защиты от компилятора при форсировании / указании того, что ошибка может быть выдана
— Обработка ошибок совершенно необязательна
Java Way
Затем пришла java и пришла с настройом, позволившим мне исправить всю обработку ошибок, и они изобрели проверенное / непроверенное исключение.
Посмотрите на фрагмент кода
1
2
3
4
5
6
7
|
public void close() { try { this .writer.close(); } catch (Exception e) { throw new RuntimeException(e); } } |
Этот подход имеет все больше проблем
— Код заполнен многословием кода обработки ошибок
— Компилятор заставляет вас обрабатывать проверенное исключение неправильно (т.е. просто регистрировать его или игнорировать)
— Ничего осмысленного не делается, кроме как что-то записать в журнал и обернуть в RuntimeExcetion, чтобы получить пропущенный компилятор.
— Оборачивание ухудшает ситуацию, потому что вы начинаете терять контекст того, что вызвало ошибку
Способ функционального программирования
Этот мир должен делать лучше, чем «императивный» мир и что они сделали? Изобретенные монады.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
def main(args: Array[String]): Unit = { val res = divide( 10 , 0 ) res match { case Left(value) => println(s "Result is ${value}" ) case Right(e) => println(s "Calculation failed reason ${e}" ) } val result = trydivide( 10 , 0 ) result match { case Success(r) => println(s "Result ${r}" ) case Failure(error) => println(s "Failed to calculate reason : ${error.getMessage}" ) } } |
Что следует учитывать при использовании этого подхода
— Вы должны выучить причудливый технический жаргон теории категорий или монад
— Теперь дает 2 значения, и вы должны написать немного меньше подробного кода, чтобы обрабатывать оба пути
— Проблемы с производительностью из-за дополнительной оборачиваемости стоимости, и когда у вас их миллионы, это очень сильно бьет вас
— Нет безопасности во время компиляции, у вызывающей стороны есть возможность обойти это путем непосредственного получения значения
— и я думаю, что это была попытка исправить значение Optional / MayBe, в котором вы не знаете, почему значение не доступно.
— Stacktrace ушел, и в некоторых случаях он полезен, особенно для построения системы, которая вызывает сторонние библиотеки
Go Lang Way
GoLang хотел добиться большего успеха, чем C / Java, и черпал вдохновение из языка Elm и придумал подход с делегированием или Killer (т.е. Panic)
1
2
3
4
|
listener, listenError := net.ListenTCP( "tcp" , addr) if listenError != nil { return nil, fmt.Errorf( "Listen: %s" , listenError) } |
Это интересный подход, но
— с ошибкой возврата в самом вызове функции, вызывающая сторона должна добавить код обработки ошибок
— Трассировка потеряна, поэтому вы должны быть очень осторожны при добавлении всего контекста к сообщению, чтобы было возможно восстановление.
— Паника не подходит для библиотеки или фреймворка, потому что вы не можете убить процесс, это должно быть ответственностью клиента, чтобы решить, что делать.
JavaScript / Python путь
Я оставлю это сейчас.
Нет ясного победителя, в каком варианте лучше, и каждый язык делает некоторый компромисс. Мы не исключение, как java и как Go Lang, это 2 маятник.
Что может быть хорошо, так это возможность вызывающего абонента решить, какой подход использовать, это может быть Java Style или Go Lang.
Лучший способ разделения потока управления и ошибки, потому что в некоторых случаях значение по умолчанию для ошибки может быть хорошим вариантом или просто очистка / восстановление или отправка на верхний уровень для лучшей обработки.
Таким образом, код в блоке catch много говорит о том, чего хочет клиент, и это должно решить, какой шаблон обработки ошибок следует использовать.
Я думаю, что это больше об образовании о том, что правильно в контексте и использовании шаблона.
Рад узнать, что вы думаете об обработке ошибок и как это должно быть сделано.
Смотрите оригинальную статью здесь: обработка исключений
Мнения авторов .NET Code Geeks — их собственные. |