Вы бросаете исключение аргумента или исключение аргумента из частных методов?

20

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

Я предполагаю, что мое обоснование заключается в том, что это помогает в будущем проверить приложение, если кто-то попытается «злоупотребить» методом в будущем. Однако, учитывая, что это частный метод, и люди, которые могут вызвать этот метод, могут видеть связанные комментарии и код, просто не нужно его выбрасывать. Это конечно не повредит иметь их, хотя это добавляет беспорядок.

Мне кажется, что эти исключения, как правило, более полезны для чего-то вроде API, который будет представлен публично.

Мистер лось
источник

Ответы:

22

Обычно для частных методов вы не генерируете исключения, поскольку, как вы написали, разработчик должен знать, как и откуда он вызывает метод. Таким образом, переменные, передаваемые в качестве параметров в закрытый метод, должны проверяться вне метода, то есть перед его вызовом. Создание «InvalidArgumentException» и других подобных исключений считается хорошей практикой для открытых методов (независимо от того, пишете ли вы «API» или нет).

В тех случаях, когда вы хотите выбросить «InvalidArgumentException», стоит упомянуть, что Assertв Spring API для Java существует класс начиная с версии 1.1.2. Это было очень полезно - по крайней мере для меня - в написании меньшего количества кода для выполнения проверок.

Однако вы можете использовать «asserts» для проверки параметров в приватных методах. Это одна из их истинных целей. Они являются более вескими причинами для их использования, просмотрите следующую ссылку, которая также подробно объясняет, когда использовать утверждения, а когда использовать исключения. Утверждения не должны быть включены в производственный код, и компилятор удаляет их по умолчанию. Таким образом, они - то, что вы ищете: помощь разработчикам, незаметная для пользователей. В Java вы должны использовать специальный флаг (-ea), чтобы указать компилятору включить утверждения. Вы можете считать их "отлаживающими" друзьями.

Вот как использовать утверждения в:

Jalayn
источник
1
Apache Commons также имеет класс Validate, который функционирует аналогично классу Spring Assert
Роза Рихтер
@cantido yes, и я также добавил бы, что класс Preconditions в Google Guava работает точно так же. Спасибо за информацию :-)
Jalayn
2

Как и все остальное, это зависит ....

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

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

Кен Хендерсон
источник
2

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

Программисты на C ++ обычно интересуются тремя вещами:

  • Будет ли это иметь нулевые издержки в оптимизированных сборках? (То есть можно ли его «составить»?)
  • Могу ли я использовать его для отладки прямо в том месте, где была обнаружена ошибка?
  • Могу ли я использовать его для сообщения о проблемах от объявленных функций noexcept?

В прошлом я подошел к первой проблеме, написав такой код

int
factorial(const int n)
{
  if (CHECK_ARGS)
    {
      if (n < 0)
        throw std::invalid_argument {"n < 0"};
    }
  int fac = 1;
  for (int i = 2; i <= n; ++i)
    fac *= i;
  return fac;
}

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

Мне все еще нравится в этом решении то, что код проверки аргументов хорошо виден, сгруппированный в if. Тем не менее, второй и третий вопрос не решаются этим. Поэтому теперь я снова склоняюсь к использованию assertмакроса для проверки аргументов.

Стандарты кодирования Boost согласны с этим:

Как насчет ошибок программиста?

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

Был очень интересный доклад Джона Лакоса на CppCon'14 под названием « Защитное программирование сделано правильно» ( часть 1 , часть 2 ). В первой части своего выступления он обсуждает теорию контрактов и неопределенного поведения. Во второй части он представляет то, что я считаю очень хорошим предложением для систематической проверки аргументов. По сути, он предлагает макросы утверждений, которые позволяют пользователю выбирать, какой объем бюджета (с точки зрения использования ЦП) он готов пожертвовать библиотеке для проверки аргументов, и позволяет ли библиотека разумно использовать этот бюджет. Кроме того, пользователь также может установить глобальную функцию обработки ошибок, которая будет вызываться в случае обнаружения нарушенного контракта.

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

5gon12eder
источник
1

Рассмотрим следующую структуру:

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

  2. Пользовательский интерфейс обертка: Эта функция оборачивает внутреннюю функцию и использует InvalidArgumentExceptions для обработки ошибочных значений и сказать пользователю , чтобы исправить свои входы: Assert(x).hasLength(4);, Assume(y).isAlphanumeric();, Assert(z).isZipCode();, Assume(mailAdress).matchesRegex(regex_MailAdress);, Reject(x).ifEmpty();и т.д.

  3. Пакетная оболочка интерфейса: эта функция оборачивает внутреннюю функцию и использует ведение журнала, метки достоверности и статистику для обработки неправильных значений без прерывания какой-либо длительной задачи. Маркировка может быть использована позже кем-то, кто проверяет и очищает базу данных результатов.

  4. Оболочка интерфейса командной строки: эта функция оборачивает внутреннюю функцию и снова запрашивает последний ввод.

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

Ральф К.
источник
0

Существуют более эффективные способы избежать проверки пустых ссылок: используйте контракт кода или инфраструктуру AOP, чтобы выполнить проверку за вас. Google "C # код контракта" или "postsharp".

Codism
источник
Я предполагаю, что мой вопрос будет распространяться и на контракты по коду. Есть ли необходимость в проверке предварительных условий метода в частном методе (т. Е. Для проверки в будущем или для того, чтобы не выстрелить себе в ногу)?
Мистер Мус