Catch Me If You ... Can't Do Otherwise

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

Я не знаю, является ли это анти-паттерном или просто распространенной и очень популярной ошибкой, но я вижу это повсюду и просто должен написать об этом. Речь идет о перехвате исключений без повторного выбрасывания. Я говорю о чем-то вроде этого Java-кода:

Обратите внимание: у меня нет ничего против этого кода.

Это называется цепочка исключений и является абсолютно допустимой конструкцией.

Итак, что не так с перехватом исключения и его журналированием? Давайте сначала попробуем рассмотреть более крупную картину. Мы говорим об объектно-ориентированном программировании - это означает, что мы имеем дело с объектами. Вот как может выглядеть объект (точнее, его класс):

Вот как я использую этот класс:

Выглядит красиво, верно? Мне не нужно беспокоиться о IOException, когда я вызываю send(1). Он будет обработан внутренне, и, если возникнет, будет зарегистрирован стек вызовов. Но это совершенно неправильное мышление, и оно унаследовано от языков без исключений, таких как C.

Исключения были изобретены для упрощения нашего дизайна путем переноса всего кода обработки ошибок из основной логики. Более того, мы не просто переносим его, но и сосредотачиваем его в одном месте — в методе main(), входной точке всего приложения.

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

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

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

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

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

К сожалению, дизайн Java идет вразрез с этим принципом во многих местах. Например, в Java есть проверяемые и непроверяемые исключения, хотя по моему мнению должны быть только проверяемые (те, которые вы должны перехватывать или объявлять как перечисляемые). Кроме того, Java позволяет объявлять несколько типов исключений как перечисляемые в одном методе - еще одна ошибка; придерживайтесь объявления только одного типа. Также, в иерархии есть обобщенный класс Exception, который также неправильный по моему мнению. Кроме того, некоторые встроенные классы не позволяют выбрасывать проверяемые исключения, например, Runnable.run(). Есть много других проблем с исключениями в Java.

Но попытайтесь помнить об этом принципе, и ваш код будет чище: catch только если у вас нет другого выбора.

P.S. Вот как должен выглядеть класс:

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-27 at 05:20

sixnines availability badge   GitHub stars