Подтвердить что ты не робот

В чем преимущество цепочки исключений

Я не понимаю преимуществ наличия связанного исключения в коде.

Учитывая пример ResourceLoader из java-мира, если программист знает о возможности встретить ResourceLoadException, почему бы не поймать одно и то же исключение вместо SQLException? Else, программист может уловить оба исключения в том же коде, чем бросать новый экземпляр Throwable?

4b9b3361

Ответ 1

Может ли кто-нибудь указать информацию о необходимости прикомандированных исключений?

В статье говорится об этом довольно хорошо:

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

То есть, если у вас есть метод, который загружает какой-либо объект из базы данных, скорее всего, вы захотите немного ResourceLoadException (ближе к уровню абстракции методов) вместо низкоуровневого SQLException, даже если это был исходный источник проблемы. Однако, если вы просто поймаете SQLException и выбросите ResourceLoadException вместо этого, вы можете потерять важную информацию для отладки.

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

Иначе программист может уловить оба исключения в одном и том же коде, чем бросать новый экземпляр Throwable?

Я не совсем понимаю ваши рассуждения. Дело в том, что ему не нужно беспокоиться о SQLException на этом уровне абстракции.

Ответ 2

Почему исключение цепочки?

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

Возьмем следующие примеры 1. без цепочки и 2. цепочки, исключения, чтобы почувствовать разницу

Создать следующие исключения

    class NoLeaveGrantedException extends Exception {

        public NoLeaveGrantedException(String message, Throwable cause) {
            super(message, cause);
        }

        public NoLeaveGrantedException(String message) {
            super(message);
        }
    }

    class TeamLeadUpsetException extends Exception {

        public TeamLeadUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public TeamLeadUpsetException(String message) {
            super(message);
        }
    }

    class ManagerUpsetException extends Exception {

        public ManagerUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public ManagerUpsetException(String message) {
            super(message);
        }
    }

    class GirlFriendOfManagerUpsetException extends Exception {

        public GirlFriendOfManagerUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public GirlFriendOfManagerUpsetException(String message) {
            super(message);
        }
    }

Теперь используйте их

1. Без привязки

    public class MainClass {

        public static void main(String[] args) throws Exception {
            getLeave();
        }

        static void getLeave() throws NoLeaveGrantedException {
            try {
                howIsTeamLead();
            } catch (TeamLeadUpsetException e) {
                e.printStackTrace();
                throw new NoLeaveGrantedException("Leave not sanctioned.");
            }
        }

        static void howIsTeamLead() throws TeamLeadUpsetException {
            try {
                howIsManager();
            } catch (ManagerUpsetException e) {
                e.printStackTrace();
                throw new TeamLeadUpsetException(
                            "Team lead is not in good mood");
            }
        }

        static void howIsManager() throws ManagerUpsetException {
            try {
                howIsGirlFriendOfManager();
            } catch (GirlFriendOfManagerUpsetException e) {
                e.printStackTrace();
                throw new ManagerUpsetException("Manager is in bad mood");
            }

        }

        static void howIsGirlFriendOfManager() 
             throws GirlFriendOfManagerUpsetException {
            throw new GirlFriendOfManagerUpsetException(
             "Girl friend of manager is in bad mood");
        }
    }

2. Цепной

    public class MainClass {

        public static void main(String[] args) throws Exception {
            getLeave();
        }

        static void getLeave() throws NoLeaveGrantedException {
            try {
                howIsTeamLead();
            } catch (TeamLeadUpsetException e) {
                throw new NoLeaveGrantedException("Leave not sanctioned.", e);
            }
        }

        static void howIsTeamLead() throws TeamLeadUpsetException {
            try {
                howIsManager();
            } catch (ManagerUpsetException e) {
                throw new TeamLeadUpsetException(
                           "Team lead is not in good mood", e);
            }
        }

        static void howIsManager() throws ManagerUpsetException {
            try {
                howIsGirlFriendOfManager();
            } catch (GirlFriendOfManagerUpsetException e) {
                throw new ManagerUpsetException("Manager is in bad mood", e);
            }

        }

        static void howIsGirlFriendOfManager() 
           throws GirlFriendOfManagerUpsetException {
            throw new GirlFriendOfManagerUpsetException(
              "Girl friend of manager is in bad mood");
        }
    }

Теперь сравните журналы

1. Без привязки

    com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:61)
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:55)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:46)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:37)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)

2. Цепной

    Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:36)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    Caused by: com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:44)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        ... 1 more
    Caused by: com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:42)
        ... 2 more
    Caused by: com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:58)
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:50)
        ... 3 more

Ответ 3

Преимущество состоит в том, что вызывающий абонент должен обрабатывать ResourceLoadException вместо SQLException. Таким образом, если позднее вы замените хранилище данных на файл, доступ к которому может привести к возникновению IOException. Вам не нужно возвращаться и изменять тип Exception, который обрабатывает ваш вызывающий. Это полезно для вашего вызывающего абонента, потому что вызывающий объект будет обрабатывать оба исключения одинаково.

Ответ 4

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

Все, что вам нужно учитывать при вызове loadResource, это может вызвать исключение ResourceLoadException. Не то, что детали реализации терпят неудачу из-за SQLException - это тоже может измениться со временем, позже кто-то может решить загрузить ресурсы из другого места, которые также могут потерпеть неудачу.

Вам просто нужно загрузить некоторые ресурсы, вам нужно обработать, если это не удается, а не обрабатывать потенциальные ошибки MainframeHasCrashedException, FileNotFoundException и еще десяток других причин, по которым загрузка этих ресурсов может завершиться неудачей.

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

Вы не должны испытывать соблазн просто перехватывать исключение здесь, если loadResources также могут вызывать другие исключения, например. UnautorizedException, вы можете не захотеть иметь дело с этим при вызове loadResources - вы можете распространять это исключение до других вызывающих абонентов, которые могут иметь дело с UnautorizedException (и, возможно, запрашивать у пользователя некоторые учетные данные). catch (Exception e) проглотит это UnautorizedException, с которым вы действительно не справитесь.

Ответ 5

Первое преимущество - инкапсуляция. ResourceLoader может быть интерфейсом с несколькими реализациями (например, один загружает ресурсы из базы данных, а другой загружает их из файловой системы), где выбранная реализация выбирается во время выполнения. Затем вызывающий абонент должен быть несовместим с основной причиной того, почему загрузка ресурса не удалась, но может по-прежнему реагировать на сбои загрузки ресурсов. Это особенно полезно, если интерфейс объявляет два разных исключения, на которые вызывающий может пожелать реагировать по-разному (например, исключение TransientResourceLoadFailureException, в котором может повториться повторная попытка, и исключение PermanentResourceLoadFailureException, где, как известно, повторная попытка не выполняется).

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

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

Ответ 6

Я думаю, что причина из этого примера заключается в том, что автор хочет создать единый интерфейс, который не зависит от какой-либо конкретной базовой системы, такой как SQL. Следовательно, он преобразует исключение в более общую форму, оставив фактическую реализацию прозрачной для бизнес-логики.

Однако в некоторых случаях может быть желательно распространять ошибку, такую ​​как SQLException. Это будет полезно в тех местах, где нужно принимать разные действия на основе брошенного исключения.