Множественная конкретная добыча или одна ловит всех? - PullRequest
7 голосов
/ 31 августа 2010

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

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

try {
  /* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException e) {
    throw new MyAPIException("Something went wrong", e);
} catch (FactoryException e) {
    throw new MyAPIException("Something went wrong", e);
} catch (MismatchedDimensionException e) {
    throw new MyAPIException("Something went wrong", e);
} catch (TransformException e) {
    throw new MyAPIException("Something went wrong", e);
}

... и, как мы видим, я просто обертываю эти исключения и выкидываю новый, говорящий, чточто-то пошло не так в моем API.

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

try {
  /* some code that throws these exceptions */
} catch (Exception e) {
    throw new MyAPIException("Something went wrong", e);
}

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

try {
  /* some code that throws some exceptions */
} catch (RuntimeException e) {
    throw e;
} catch (Exception e) {
    throw new MyAPIException("Something went wrong", e);
}

Это немного громоздко, но это помогает.Теперь есть еще одна небольшая проблема, связанная с перехватом (исключение e), что если мой внутренний API генерирует другое MyAPIException, оно также будет перехвачено, упаковано и выброшено в другом MyAPIException.В этом конкретном случае мы могли бы также перехватить MyAPIException и перезапустить его.

try {
  /* some code that throws some exceptions */
} catch (RuntimeException e) {
    throw e;
} catch (MyAPIException e) {
    throw e;
} catch (Exception e) {
    throw new MyAPIException("Something went wrong", e);
}

Что ж, это снова становится грязным, но в этом случае мы предотвращаем упаковку MyAPIException и просто перекидываем его.Но есть и еще одна проблема с блоком catch (Exception e), заключающаяся в том, что если внутренний API изменяется и начинает выдавать другое исключение (некоторые, кроме этих 4, упомянутых выше), компилятор ничего не скажет об этом, и мы не будемПонятия не имею.Не то чтобы это было серьезной проблемой, поскольку, вероятно, я бы отнесся к этому таким же образом.

При таком сценарии я думаю, что вопрос в том, какой из них лучше, и есть ли лучшие варианты?

Ответы [ 6 ]

5 голосов
/ 01 сентября 2010

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

Useage

try
{
     // some code that might throw one of several exceptions
}
catch ( Exception cause )
{
     MyAPIException . handle ( cause ) ;
}

MyAPIException содержит логику

class MyAPIException extends Exception
{
    private MyAPIException ( String message , Throwable cause ) { super ( message , cause ) ; }

    private static void myAPIException ( Exception cause ) throws MyAPIException
    {
         throw new MyAPIException ( "Something Went Wrong" , cause ) ;
    }

    public static void handle ( Exception e ) throws MyAPIException
    {
          try
          {
               throw ( e ) ;
          }
          catch ( RuntimeException cause )
          {
                throw cause ;
          }
          catch ( MyAPIException cause )
          {
                 throw cause ;
          }
          catch ( NoSuchAuthorityCodeException cause )  // repeat for other exceptions
          {
                 myAPIException ( cause ) ;
          }
          catch ( Exception cause ) // this should not happen
          {
                  assert false ; // or log it or throw a RuntimeException ... somehow signal that programming logic has failed
          }
    }
}
2 голосов
/ 01 сентября 2010

В JAVA 7 вы сможете сделать что-то вроде

try {
  /* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException , FactoryException, MismatchedDimensionException , TransformException e) {
    throw new MyAPIException("Something went wrong", e);
}

Так что, возможно, лучший ответ - это немного отдохнуть, а затем перейти на JAVA 7, когда он станет доступен.

2 голосов
/ 31 августа 2010

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

Кроме того, вы не можете исправить ни простую ловушку, ни множественную ловушку.Это скорее субъективное решение IMO, так как есть исключения, которые являются фатальными (поскольку во всей программе приходится останавливаться) для более простых в обращении типов.

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

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

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

1 голос
/ 09 сентября 2010

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

Во-первых, у нас есть абстрактный класс, называемый ExceptionHandler.

package my.api.pack;

import java.lang.reflect.ParameterizedType;
import java.util.LinkedHashSet;
import java.util.Set;

public abstract class ExceptionHandler<E extends Exception> {

    Set<Class<? extends Exception>> exceptionClasses = new LinkedHashSet<Class<? extends Exception>>();

    protected abstract void handler(Throwable t) throws E;

    public ExceptionHandler<E> catches(Class<? extends Exception> clazz) {
        exceptionClasses.add(clazz);
        return this;
    }

    @SuppressWarnings("unchecked")
    private Class<E> getGenericsClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<E>) parameterizedType.getActualTypeArguments()[0];
    }

    @SuppressWarnings("unchecked")
    public final void handle(Throwable t) throws E, UnhandledException {
        if (getGenericsClass().isInstance(t)) {
            throw (E) t;
        }

        for (Class<? extends Exception> clazz : exceptionClasses) {
            if (clazz.isInstance(t)) {
                handler(t);
                return;
            }
        }

        throw new UnhandledException("Unhandled exception", t);
    }

}

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

package my.api.pack;

public class UnhandledException extends RuntimeException {

    private static final long serialVersionUID = -3187734714363068446L;

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


}

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

try {
  /* some code that throws these exceptions */
} catch (Exception e) {
    new ExceptionHandler<MyAPIException>() {
        @Override
        protected void handler(Throwable t) throws MyAPIException {
            throw new MyAPIException("Something went wrong", t);
        }
    }.
        catches(MismatchedDimensionException.class).
        catches(NoSuchAuthorityCodeException.class).
        catches(FactoryException.class).
        catches(TransformException.class).
        handle(e);
    return null;
}

Что вы, ребята, думаете?

0 голосов
/ 11 сентября 2010

Ниже приведен другой подход и пример использования.У класса MyAPIException есть метод handle.Метод handle будет обрабатывать любое Исключение.

  1. Если Исключение является Runnable или MyAPIException, дескриптор сгенерирует его без переноса.
  2. Если нет, метод handle проверит, является лиутверждения включены.Если утверждения включены, метод handle будет проверять, назначается ли исключение из одного из поддерживаемых типов исключений, и выдает AssertionError, если оно не присваивается.(Если утверждения не включены, метод handle игнорирует параметр поддерживаемых типов исключений.)
  3. Наконец, если он достигнет этой точки, метод handle поместит исключение в MyAPIException и сгенерирует его.

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

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

class MyAPIException extends Exception
{
    private static final long serialVersionUID = 0 ;

    MyAPIException ( Throwable cause )
    {
        super ( cause ) ;
    }

    static void handle ( Exception cause , Class < ? > ... supportedExceptionTypes ) throws MyAPIException
    {
        try
        {
        throw ( cause ) ;
        }
        catch ( RuntimeException e )
        {
        throw ( e ) ;
        }
        catch ( MyAPIException e )
        {
        throw ( e ) ;
        }
        catch ( Exception e )
        {
        search :
        try
            {
            assert false ;
            }
        catch ( AssertionError a )
            {
            for ( Class < ? > c : supportedExceptionTypes )
                {
                if ( c . isAssignableFrom ( e . getClass ( ) ) )
                    {
                    break search ;
                    }
                }
            assert false : e + " is not one of the supportedExceptionTypes : " + supportedExceptionTypes ;
            }
        MyAPIException error = new MyAPIException ( e ) ;
        throw ( error ) ;
        }
    }
}

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

class Usage
{
    public static void main ( String [ ] args ) throws MyAPIException
    {
    try
        {
        switch ( Integer . parseInt ( args [ 0 ] ) )
            {
            case 1 :
            throw new RuntimeException ( ) ;
            case 2 :
            throw new SQLException ( ) ;
            case 3 :
            throw new MyAPIException ( null ) ;
            case 4 :
            throw new IOException ( ) ;
            }
        }
    catch ( Exception cause )
        {
        System . out . println ( cause . getMessage ( ) ) ;
        System . out . println ( cause . getCause ( ) ) ;
        MyAPIException . handle ( cause , IOException . class ) ;
        }
    }
}
0 голосов
/ 02 сентября 2010

Все зависит от того, что ты хочешь делать.Если все, что вы хотите сделать, это throw new MyException("Something went wrong", e);, то все поймает один улов.

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