Java исключения обработки идиом ... кто прав и как с этим справиться? - PullRequest
7 голосов
/ 04 марта 2009

У меня в настоящее время есть техническая разница со знакомым. В двух словах, это разница между этими двумя основными стилями обработки исключений Java:

Вариант 1 (мой):

try {
...
} catch (OneKindOfException) {
...
} catch (AnotherKind) {
...
} catch (AThirdKind) {
...
}

Вариант 2 (его):

try {
...
} catch (AppException e) {
    switch(e.getCode()) {
    case Constants.ONE_KIND:
    ...
    break;
    case Constants.ANOTHER_KIND:
    ...
    break;
    case Constants.A_THIRD_KIND:
    ...
    break;
    default:
    ...
    }
}

Его аргумент - после того, как я использовал обширные ссылки о проверке пользовательского ввода, обработке исключений, утверждениях и контрактах и ​​т. Д. Для подтверждения своей точки зрения - сводился к следующему:

"Это хорошая модель. Я использовал ее с тех пор, как я и мой друг придумали ее в 1998 году, почти 10 лет назад. Посмотрите еще раз, и вы увидите, что мы пошли на компромиссы с академическими аргументами иметь много смысла. "

Есть ли у кого-нибудь аргумент о том, почему вариант 1 - это путь?

Ответы [ 11 ]

19 голосов
/ 04 марта 2009

Когда у вас есть оператор switch, вы менее объектно-ориентированы. Есть также больше возможностей для ошибок, забыв о выражении "break;", забывая добавить регистр для Exception, если вы добавляете новый брошенный Exception.

Я также нахожу, что ваш способ сделать это НАМНОГО более читабелен, и это стандартная идиома, которую сразу поймут все разработчики.

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

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

6 голосов
/ 04 марта 2009
  • способ кодирования варианта 2, любой неожиданный тип исключения будет проглочен! (это можно исправить повторным броском в случае по умолчанию, но это, возможно, уродливая вещь - гораздо лучше / эффективнее, чтобы не поймать ее с самого начала)
  • вариант 2 - это ручное воссоздание того, что вариант 1, скорее всего, делает под капотом, т. Е. Он игнорирует предпочтительный синтаксис языка для использования старых конструкций, которых лучше избегать из соображений обслуживания и удобства чтения. Другими словами, в варианте 2 изобретается колесо с использованием более уродливого синтаксиса, чем в языковых конструкциях.

понятно, оба способа работают; опция 2 просто устарела из-за более современного синтаксиса, поддерживаемого опцией 1

4 голосов
/ 04 марта 2009

С опцией 1 вызывающая сторона имеет возможность выбрать, какое именно исключение перехватить, и игнорировать все остальные. При выборе варианта 2 вызывающая сторона должна помнить о необходимости перебрасывать любые исключения, которые не были явно перехвачены.

Кроме того, имеется лучшая самодокументирование с вариантом 1, поскольку сигнатура метода должна точно указывать, какие исключения выдается, а не одно исключение переопределения.

Если необходимо иметь всеобъемлющее AppException, другие типы исключений всегда могут наследоваться от него.

4 голосов
/ 04 марта 2009

Я не знаю, есть ли у меня аргумент опровержения, но первоначальные мысли

  • Вариант 2 работает до тех пор, пока вы не попытаетесь отловить исключение, которое не реализует getCode ()
  • Вариант 2 побуждает разработчика отлавливать общие исключения, это проблема, потому что если вы не реализуете оператор case для данного подкласса AppException, компилятор не предупредит вас. Конечно, вы можете столкнуться с той же проблемой с вариантом 1, но по крайней мере вариант 1 не поощряет это активно.
3 голосов
/ 04 марта 2009

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

  1. Произошло непредвиденное состояние, которое не может быть обработано здесь.
  2. Кто-то будет заботиться о трассировке стека.

Как насчет третьего пути? Вы можете использовать enum для типа ошибки и просто вернуть его как часть типа метода. Для этого вы должны использовать, например, Option<A> или Either<A, B>.

Например, у вас будет:

enum Error { ONE_ERROR, ANOTHER_ERROR, THIRD_ERROR };

и вместо

public Foo mightError(Bar b) throws OneException

у вас будет

public Either<Error, Foo> mightError(Bar b)

Бросок / улов немного похож на goto / comefrom. Легко злоупотреблять. См. Перейти к оператору, который считается вредным. и Ленивая обработка ошибок

3 голосов
/ 04 марта 2009

Аргумент нокдауна будет заключаться в том, что он нарушает инкапсуляцию, так как теперь мне нужно кое-что знать о подклассе открытого интерфейса Exception, чтобы обрабатывать им исключения. Хорошим примером этой «ошибки» в JDK является java.sql.SQLException, предоставляющий методы getErrorCode и getSQLState.

1 голос
/ 04 марта 2009

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

С помощью опции 2 вы полностью скрываете значение исключительных условий в вашем коде. Не следует сворачивать различные состояния ошибки в одно общее исключение AppException, если только они никогда не будут обрабатываться по-разному. Тот факт, что вы переходите на getCode(), указывает на иное, поэтому используйте разные исключения для разных исключений.

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

1 голос
/ 04 марта 2009

Я думаю, это зависит от того, в какой степени это используется. У меня, конечно, не было бы «одного исключения, чтобы управлять ими всеми», которое выбрасывают все. С другой стороны, , если , существует целый класс ситуаций, которые почти наверняка будут обрабатываться одинаково, но вам, возможно, придется различать их для (скажем) целей обратной связи с пользователем, вариант 2 будет имеет смысл только для этих исключений . Они должны быть очень узкими по объему - так, чтобы, когда имеет смысл бросать один «код», он, вероятно, имел бы смысл и для всех остальных.

Критическим тестом для меня было бы: "будет ли когда-нибудь смысл поймать AppException с одним кодом, но хотите, чтобы другой код оставался необработанным?" Если это так, они должны быть разных типов.

0 голосов
/ 04 марта 2009

Используйте оба.

Первый для большинства исключений в вашем коде.

Второе для тех самых "особых" исключений, которые вы создали.

Не борись с такими мелочами.

Кстати, 1-е лучше.

0 голосов
/ 04 марта 2009
  1. Излишне знать код и объявлять константы для исключения, которое могло быть абстрактным при использовании опции 1.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...