вторник, 23 мая 2017 г.

re-throwing

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


Я говорю о чем-то вроде этого Java-кода:
try {
  stream.write(data);
} catch (IOException ex) {
  ex.printStackTrace();
}
Обратите внимание: я ничего не имею против этого кода:
try {
  stream.write('X');
} catch (IOException ex) {
  throw new IllegalStateException(ex);
}
Это называется цепочкой исключений и является совершенно верной конструкцией.

Итак, что не так с перехватом исключения и протоколированием его? Давайте попробуем сначала взглянуть на большую картинку. Мы говорим об объектно-ориентированном программировании - это означает, что мы имеем дело с объектами. Вот как выглядел бы объект (его класс, если быть точным):
final class Wire {
  private final OutputStream stream;
  Wire(final OutputStream stm) {
    this.stream = stm;
  }
  public void send(final int data) {
    try {
      this.stream.write(x);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}
Вот как я использую этот класс:
new Wire(stream).send(1);
Выглядит красиво, не так ли? Мне не нужно беспокоиться об этом исключении IOException, когда я вызываю send (1). Он будет обрабатываться изнутри, и если это произойдет, трассировка стека будет регистрироваться. Но это совершенно неправильный способ мышления, и он унаследован от языков без исключений, таких как C.

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

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

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

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

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

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

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

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

Постскриптум Вот как должен выглядеть класс:
final class Wire {
  private final OutputStream stream;
  Wire(final OutputStream stm) {
    this.stream = stm;
  }
  public void send(final int data)
    throws IOException {
    this.stream.write(x);
  }
}

Комментариев нет:

Отправить комментарий