Соглашения об исключениях или кодах ошибок - PullRequest
106 голосов
/ 31 октября 2008

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

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

Ответы [ 22 ]

4 голосов
/ 31 октября 2008

Я могу сидеть здесь на заборе, но ...

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

В Python использование исключений является стандартной практикой, и я очень рад определить свои собственные исключения. В Си вообще нет исключений.

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

Основой кода, над которой я работаю, в основном является C ++, и мы используем коды ошибок почти везде, но есть один модуль, который вызывает исключения для любых ошибок, в том числе очень необычных, и весь код, который использует этот модуль, довольно ужасен. Но это может быть только потому, что мы смешали исключения и коды ошибок. Код, который последовательно использует коды ошибок, намного легче работать. Если бы в нашем коде последовательно использовались исключения, возможно, это было бы не так плохо. Смешение этих двух не очень хорошо работает.

3 голосов
/ 31 октября 2008

Подписи метода должны сообщать вам, что делает метод. Что-то вроде long errorCode = getErrorCode (); может быть хорошо, но long errorCode = fetchRecord (); сбивает с толку.

3 голосов
/ 31 октября 2008

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

Я привык определять несколько типов исключений (например, DataValidationException или ProcessInterruptExcepion) и внутри каждого исключения определяю более подробное описание каждой проблемы.

Простой пример на Java:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

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

3 голосов
/ 31 октября 2008

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

Во всех других случаях исключения - это, вероятно, путь.

2 голосов
/ 27 марта 2017

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

В SOA, где методы могут вызываться на разных машинах, исключения не могут передаваться по проводам, вместо этого мы используем ответы об успехе / неудаче со структурой, подобной приведенной ниже (C #):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

И используйте вот так:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

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

2 голосов
/ 31 октября 2008

Исключения относятся к исключительным обстоятельствам, т. Е. Когда они не являются частью нормального потока кода.

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

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

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

Но, идя в другом направлении, используя исключения, вы сможете создавать абстракции еще более высокого уровня для обработки ошибок, которые могут сделать ваш код еще более выразительным и естественным. Я очень рекомендую прочитать эту превосходную, но недооцененную статью эксперта C ++ Андрея Александреску на тему того, что он называет «Принуждения»: http://www.ddj.com/cpp/184403864. Хотя это статья C ++, принципы в целом применимы, и я перевел концепция применения к C # довольно успешно.

1 голос
/ 22 марта 2014

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

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

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

1 голос
/ 14 октября 2013

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

Возвращение недопустимых указателей, которые могут быть разыменованы (например, вызывает исключение NullPointerException в Java), недопустимо.

Использование нескольких различных числовых кодов ошибок (-1, -2) в качестве возвращаемых значений для одной и той же функции обычно является плохим стилем, поскольку клиенты могут выполнить проверку "== -1" вместо "<0". </p>

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

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

В любом случае, когда возможно более одного тривиального типа ошибки, исходный код никогда не должен использовать числовой литерал, чтобы вернуть код ошибки или обработать его (вернуть -7, если x == -7 ... ), но всегда именованная константа (верните NO_SUCH_FOO, если x == NO_SUCH_FOO).

0 голосов
/ 15 мая 2017

Мое общее правило:

  • В функции может появиться только одна ошибка: используйте код ошибки (в качестве параметра функции)
  • Может появиться более одной конкретной ошибки: сгенерировать исключение
0 голосов
/ 03 марта 2016

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

например. Когда вы обрабатываете файл и сталкиваетесь с IOException, клиент может быть заинтересован в том, чтобы узнать, откуда это было инициировано, в открытии файла или в синтаксическом анализе файла и т. Д. Поэтому лучше вернуть IOException или его конкретный подкласс. Тем не менее, в сценарии, как у вас есть метод входа в систему и вы хотите знать, был ли он успешным или нет, вы либо просто возвращаете логическое значение, либо для отображения правильного сообщения возвращаете код ошибки. Здесь Клиенту не интересно знать, какая часть логики вызвала этот код ошибки. Он просто знает, является ли его учетная запись недействительной или блокировка учетной записи и т.д.

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

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