Пользовательские исключения: различать через многие подклассы или один класс с поддержкой перечисления?

Я хочу реализовать свой собственный набор Exceptions для проектов, над которыми я сейчас работаю. Проект опирается на основную структуру с базовым исключением MyFrameworkException (я также пишу эту структуру).

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

В обоих случаях я:

public class MyFrameworkException   extends Exception              { /*...*/ }

Вариант 1:

public class MyProjectBaseException extends MyFrameworkException   { /*...*/ }
public class SpecificExceptionType1 extends MyProjectBaseException { /*...*/ }
public class SpecificExceptionType1 extends MyProjectBaseException { /*...*/ }
public class SpecificExceptionType1 extends MyProjectBaseException { /*...*/ }

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

Вариант 2:

public class MyProjectException extends MyFrameworkException {
  public static enum Type {
    SpecificType1, SpecificType2, SpecificType3
  }
  public MyProjectException( Type type ) { /*...*/ }
}

Здесь я всегда буду бросать MyProjectException с конкретным типом перечисления для любой возникающей проблемы. Я бы предоставил некоторый механизм, чтобы оператор switch мог выполняться на любом MyProjectException на основе перечисления типа.

Каков наилучший способ обработки исключений в проектах, особенно тех, которые используют общую инфраструктуру? Являются ли два варианта выше хорошими? Почему или почему нет? И какие еще лучшие решения?

Ответ 1

Главный недостаток варианта 2 (общее исключение + перечисление) заключается в том, что вы теряете часть полезности проверенных исключений. Метод в значительной степени должен сказать просто "что-то связанное с каркасом может пойти не так":

public void foo()
throws MyFrameworkException

... а не "x или y может пойти не так":

public void foo()
throws SomethingWentWrongException, SomethingElseWentWrongException

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

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

Изменить И поднять тупик: я предположил, что вы говорили об исключениях, которые вам действительно нужно создавать. Абсолютно бросайте стандартные исключения везде, где это имеет смысл (что почти повсеместно). Не создавайте свой собственный MyFrameworkIllegalArgumentException, например, просто используйте IllegalArgumentException (или его различные подклассы).

Ответ 2

Я бы использовал исключения, расширяющие java.lang.RuntimeException с описательными именами классов.

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

См. совет Джошуа Блоха о в пользу стандартных исключений.

Ответ 3

Я бы не наследовал от общего MyFrameworkException, если вы не предоставляете никаких функций, общих для всех проектов. Else, всегда расширяйте Exception.

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