Правильная обработка возвращаемых данных

У меня есть вопрос, связанный с правильной обработкой возвратов библиотеки DAO, которую я пишу для одного проекта. Эта библиотека, вероятно, будет использоваться другими людьми, и я хочу сделать это правильно. Как я должен иметь дело с возвращающими утверждениями функций моего DAO?

Пример 1 У меня есть функция getCustomer, которая должна возвращать String. Если запрос не возвращает никакого результата, я должен вернуть null, пустую строку или выбросить какое-то исключение?

Пример 2 У меня есть функция getCutomerList, которая возвращает значение типа ArrayList <String> . Если запрос не возвращает никакого результата, я должен вернуть null, пустой ArrayList или выбросить какое-то исключение?

Пример 3 Было обнаружено какое-то исключение SQL, что мне делать, исключить исключение или сделать try.. catch блока, где это может произойти?

Что такое "хорошая" практика или "лучшая" практика в моем случае?

Ответ 1

Кажется, ваша библиотека выполняет вызовы, подобные базам данных. Если это так, то я бы сделал именно то, что реализовано с помощью спецификации JPA 2.

Что я имею в виду, посмотрите на метод find() в JPA API и верните именно то, что они там делают.

    /**
     * Find by primary key.
     * @param entityClass
     * @param primaryKey
     * @return the found entity instance or null
     *    if the entity does not exist
     * @throws IllegalStateException if this EntityManager has been closed.
     * @throws IllegalArgumentException if the first argument does
     *    not denote an entity type or the second
     *    argument is not a valid type for that
     *    entity primary key
     */
    public <T> T find(Class<T> entityClass, Object primaryKey);

Вы видите здесь в find, который, как мне кажется, похож на ваш метод getCustomer(), он вернет null, если ни один не найден, и бросает только IllegalArgumentException, если аргумент недействителен.

Если метод find() не близок к тому, что вы хотите с помощью getCustomer(), вы должны реализовать то же поведение, что getSingleResult():

    /**
     * Execute a SELECT query that returns a single result.
     * @return the result
     * @throws EntityNotFoundException if there is no result
     * @throws NonUniqueResultException if more than one result
     * @throws IllegalStateException if called for a Java 
     *    Persistence query language UPDATE or DELETE statement
     */
    public Object getSingleResult();

Что будет бросать EntityNotFoundException, если результат не найден, NonUniqueResultException, если найдено несколько экземпляров, или IllegalStateException, если SQL неверен.

Вы должны решить, какое поведение наиболее подходит для вас.

То же самое относится к getResultList():

/**
 * Execute a SELECT query and return the query results
 * as a List.
 * @return a list of the results
 * @throws IllegalStateException if called for a Java 
 *    Persistence query language UPDATE or DELETE statement
 */   
public List getResultList();

getResultList() будет возвращать null, если ни один не найден, и исключает только исключение, если SQL является незаконным.

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


Альтернативное поведение - вернуть пустую коллекцию вместо null. Вот как Google Guava внедрили свой API, и это действительно является предпочтительным. Тем не менее, мне нравится согласованность и по-прежнему думаю, что вы должны реализовать библиотеку как можно ближе к standard.


Ресурсы

Джошуа Блох сделал видео, объясняющее, как создать хороший API и почему это важно.

Ответ 2

  • нуль. Но метод getCustomer() должен возвращать клиента. Если он возвращает String, он должен, вероятно, называться getCustomerName() или getCustomerId()
  • пустой список
  • исключение. Вероятно, оберните его исключением на уровне приложения.

Ответ 3

Пример 1: Поскольку ничего не получено, null должен быть возвращен. Альтернативно, выбор Null-Object может быть выбором.

Пример 2: Предпочитает пустой ArrayList равным null. См. "Эффективная Java". Пункт 43: Возвращает пустые массивы или коллекции, а не нули.

Пример 3: Переведите SQLException в высшую Exception и бросьте его. См. "Эффективная Java". Пункт 61: Выбросы исключений, соответствующие абстрактному

Ответ 4

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

Для 1 и 2: Примите к сведению, однако, что возврат нулей заставит клиентский код продолжать выполнение таких проверок, как следующее:

result = yourAPICall();
if(result != null){
   // do something
}

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

Для 3: Это будет зависеть от вашего дизайна API. Но во-первых, никогда не бросайте исключения на низком уровне в стек вызовов. Вы должны обернуть их в специальный класс исключений, разработанный для вашего API, чтобы ваш клиентский код должен был только перехватывать исключения API, а не различные более низкие уровни (SQLException, IOException и т.д.)

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