Переменные ошибок - это анти-шаблон или хороший дизайн?


44

Для обработки нескольких возможных ошибок, которые не должны останавливать выполнение, у меня есть errorпеременная, которую клиенты могут проверять и использовать для создания исключений. Это анти-паттерн? Есть ли лучший способ справиться с этим? Для примера этого в действии вы можете увидеть PHP mysqli API. Предположим, что проблемы видимости (средства доступа, открытая и закрытая область, переменная в классе или глобальная?) Обрабатываются правильно.


6
Это то, что try/ catchсуществует для. Кроме того, вы можете поместить ваш try/ catchгораздо дальше вверх по стеку в более подходящее место для его обработки (что позволяет разделить проблемы).
jpmc26

Что следует иметь в виду: если вы собираетесь использовать обработку на основе исключений и получаете исключение, вы не хотите показывать слишком много информации пользователю. Используйте обработчик ошибок, такой как Elmah или Raygun.io, чтобы перехватить его и показать пользователю общее сообщение об ошибке. НИКОГДА не показывайте трассировку стека или конкретное сообщение об ошибке пользователю, потому что они раскрывают информацию о внутренней работе приложения, которая может быть нарушена.
Nzall

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

3
@piedar Я создал отдельный вопрос, где это можно обсудить более свободно: programmers.stackexchange.com/questions/245255/…
Nzall

26
Общий принцип в дизайне API, который довольно далеко продвигает вас, заключается в том, чтобы всегда смотреть на то, что делает PHP, а затем делать прямо противоположное.
Филипп

Ответы:


65

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

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

Сообщения

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

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

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

Политика имени "foo" не может быть получена для пользователя "bar", на который есть ссылка в профиле пользователя.

Сравните это с кодом возврата -85. Какой из них вы бы предпочли?

Стеки вызовов

Исключения обычно также имеют подробные стеки вызовов, которые помогают отлаживать код быстрее и быстрее, а также могут регистрироваться вызывающим кодом при желании. Это позволяет разработчикам точно определить проблему с точной линией, и, следовательно, является очень мощным. Еще раз, сравните это с файлом журнала с возвращаемыми значениями (такими как -85, 101, 0 и т. Д.), Какой из них вы бы предпочли?

Сбой быстрого предвзятого подхода

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

Обертывание и развертывание исключений

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

Простота кода

Это немного сложнее объяснить, но я изучил это кодирование как с возвращаемыми значениями, так и с исключениями. Код, который был написан с использованием возвращаемых значений, обычно вызывает вызов и затем выполняет серию проверок того, что было возвращаемым значением. В некоторых случаях он будет вызывать другой метод и теперь будет иметь другую серию проверок для возвращаемых значений из этого метода. За исключением исключений, обработка исключений намного проще в большинстве, если не во всех случаях. У вас есть блоки try / catch / finally, причем среда выполнения старается изо всех сил выполнить код в блоках finally для очистки. Даже вложенные блоки try / catch / finally относительно легче отслеживать и поддерживать, чем вложенные if / else и связанные возвращаемые значения из нескольких методов.

Вывод

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

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


5
Windows возвращает значения HRESULT из функций C ++, чтобы поддерживать совместимость C в своих общедоступных API-интерфейсах (и потому, что вы попадаете в мир вреда, пытаясь маршалировать исключения через границы). Не слепо следуйте модели операционной системы, если вы пишете приложение.
Коди Грей

2
Единственное, что я хотел бы добавить к этому, это упоминание о более слабой связи. Исключения позволяют обрабатывать множество неожиданных ситуаций в наиболее подходящем месте. Например, в веб-приложении вы хотите вернуть 500 для любого исключения, к которому ваш код не был подготовлен, а не вылетать из веб-приложения. Таким образом, вам нужен какой-то улов в верхней части вашего кода (или в вашей среде). Аналогичная ситуация существует в настольных графических интерфейсах. Но вы также можете разместить менее общие обработчики в разных местах кода, чтобы обрабатывать различные ситуации сбоя таким образом, чтобы это соответствовало текущему процессу.
jpmc26

2
@TrentonMaki Если вы говорите об ошибках конструкторов C ++, лучший ответ здесь: parashift.com/c++-faq-lite/ctors-can-throw.html . Короче говоря, выкиньте исключение, но не забудьте сначала устранить потенциальные утечки. Я не знаю ни о каких других языках, где простое бросание из конструктора - это плохо. Я думаю, что большинство пользователей API предпочли бы перехватывать исключения, а не проверять коды ошибок.
Огр псалом33

3
«Такие сложные сценарии нелегко реализовать с помощью возвращаемых значений». Что вы можете! Все, что вам нужно сделать, это создать ErrorStateReturnVariableсуперкласс, и одним из его свойств является InnerErrorState(который является экземпляром ErrorStateReturnVariable), который реализует подклассы, чтобы показать цепочку ошибок ... о, подождите. : p
Брайан С

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

21

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

Конечно, если у вас есть тип ошибки, который лучше классифицировать как «предупреждение» (= ваша библиотека может выдать действительный результат, а вызывающий может проигнорировать предупреждение, если он считает, что это не важно), тогда индикатор состояния в форме переменной может иметь смысл даже в языках с исключениями. Но будьте осторожны. Призыватели библиотеки склонны игнорировать такие предупреждения, даже если они не должны. Так что подумайте дважды, прежде чем вводить такую ​​конструкцию в свою библиотеку.


1
Спасибо за объяснение! Ваш ответ + Омер Икбал ответил на мой вопрос.
Микайла Маки

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

1
@Cyanfish: да, но нужно быть осторожным, чтобы не перегружать такие вещи, особенно при создании библиотек. Лучше предоставить один простой и работающий механизм предупреждения, чем 2, 3 или более.
Док Браун

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

@Gusdor: безусловно, поэтому типичное «предупреждение» не должно вызывать ИМХО исключение по умолчанию. Но это также зависит немного от уровня абстракции. Иногда служба или библиотека просто не могут решить, следует ли рассматривать необычное событие как исключение с точки зрения вызывающих. Лично в таких ситуациях я предпочитаю, чтобы библиотека lib просто устанавливала индикатор предупреждения (не исключение), позволяла вызывающей стороне проверять этот флаг и выдавать исключение, если оно считает это целесообразным. Это то, что я имел в виду, когда писал выше: «Лучше обеспечить один механизм предупреждения в библиотеке».
Док Браун

20

Есть несколько способов сообщить об ошибке:

  • переменная ошибки для проверки: C , Go , ...
  • исключение: Java , C # , ...
  • обработчик условия: Lisp (только?), ...
  • полиморфное возвращение: Haskell , ML , Rust , ...

Проблема переменной error в том, что ее легко забыть проверить.

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

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

Полиморфные возвраты ( Either a bв Хаскеле) - мое любимое решение:

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

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


1
Хороший ответ! Я надеялся, что смогу получить список способов обработки ошибок.
Микайла Маки

Arrghhhh! Сколько раз я видел это «Проблема переменной error в том, что ее легко забыть проверить». Проблема с исключениями в том, что ее легко забыть поймать. Тогда ваше приложение вылетает. Ваш начальник не хочет платить, чтобы исправить это, но ваши клиенты перестают использовать ваше приложение, потому что они разочарованы сбоями. Все из-за чего-то, что не повлияло бы на выполнение программы, если бы коды ошибок возвращались и игнорировались. Единственный раз, когда я видел людей, игнорирующих коды ошибок, это когда это не имело большого значения. Код выше по цепочке знает, что что-то не так.
Данк

1
@Dunk: Я не думаю, что мой ответ также приносит извинения за исключения; хотя, это вполне может зависеть от типа кода, который вы пишете. Мой личный опыт работы имеет тенденцию отдавать предпочтение системам, которые терпят неудачу при наличии ошибок, потому что молчаливое повреждение данных хуже (и не обнаружено), и данные, над которыми я работаю, ценны для клиента (конечно, это также означает срочные исправления).
Матье М.

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

12

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

Вы получите код, подобный следующему:

String result = x.doActionA();
if (result != null) {
  throw new Exception(result);
}
result = x.doActionB();
if (result != null) {
  throw new Exception(result);
}

Или это:

if (!x.doActionA()) {
  throw new Exception(x.getError());
}
if (!x.doActionB()) {
  throw new Exception(x.getError());
}

Я бы предпочел, чтобы действия сами создавали исключения, так что вы получите что-то вроде:

x.doActionA();
x.doActionB();

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

Текущий (ужасный) код:

private String doActionA() {
  try {
    someOperationThatCanGoWrong1();
    someOperationThatCanGoWrong2();
    someOperationThatCanGoWrong3();
    return null;
  } catch(Exception e) {
    return "Something went wrong!";
  }
}

Новый и улучшенный:

private void doActionA() throws Exception {
  someOperationThatCanGoWrong1();
  someOperationThatCanGoWrong2();
  someOperationThatCanGoWrong3();
}

Трассировка треков сохраняется и сообщение доступно в исключении, а не в бесполезном «Что-то пошло не так!».

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


1
Однако есть одна загвоздка: в некоторых случаях ваши «новые и улучшенные» утратят контекст, в котором изначально происходит исключение. Например, в «текущей (ужасной) версии» doActionA () предложение catch будет иметь доступ к переменным экземпляра и другой информации из включающего объекта, чтобы дать более полезное сообщение.
InformedA

1
Это правда, но в данный момент здесь не происходит. И вы всегда можете перехватить исключение в doActionA и обернуть его в другое исключение сообщением о состоянии. Тогда у вас все еще будет трассировка стека и полезное сообщение. throw new Exception("Something went wrong with " + instanceVar, ex);
mrjink

Я согласен, это может не произойти в вашей ситуации. Но вы не можете «всегда» помещать информацию в doActionA (). Зачем? Вызывающий doActionA () может быть единственным, кто содержит информацию, которую вам нужно включить.
InformedA

2
Итак, пусть вызывающая сторона включает его, когда обрабатывает исключение. Это же относится и к первоначальному вопросу. Теперь вы ничего не можете сделать, если вы не можете делать с исключениями, и это приводит к более чистому коду. Я предпочитаю исключения, а не возвращаемые логические значения или сообщения об ошибках.
mrjink

Вы обычно делаете ложное предположение, что исключение, возникающее в любом из «someOperation», может быть обработано и очищено таким же образом. Что происходит в реальной жизни, так это то, что вам нужно перехватывать и обрабатывать исключения для каждой операции. Таким образом, вы не просто выбрасываете исключение, как в вашем примере. Кроме того, использование исключений затем приводит к созданию либо группы вложенных блоков try-catch, либо серии блоков try-catch. Это часто делает код гораздо менее читабельным. Я не настроен против исключений, но я использую соответствующий инструмент для конкретной ситуации. Исключения составляют всего 1 инструмент.
Данк

5

«Чтобы справиться с несколькими возможными ошибками, это не должно останавливать выполнение»

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

Два шаблона, которые я использовал:

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

  • Передайте объект обработчика ошибок / предупреждений (или установите его как переменную-член). И каждая ошибка вызывает функцию-член обработчика. Таким образом, вызывающая сторона может решить, что делать с такими не прекращающимися ошибками.

То, что вы передаете этим коллекциям / обработчикам, должно содержать достаточно контекста для того, чтобы ошибка была обработана «правильно» - строка обычно слишком мала, передавая какой-то экземпляр Exception, часто разумно - но иногда осуждаемый (как злоупотребление исключениями) ,

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

class MyFunClass {
  public interface ErrorHandler {
     void onError(Exception e);
     void onWarning(Exception e);
  }

  ErrorHandler eh;

  public void canFail(int i) {
     if(i==0) {
        if(eh!=null) eh.onWarning(new Exception("canFail shouldn't be called with i=0"));
     }
     if(i==1) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=1 is fatal");
        throw new RuntimeException("canFail called with i=2");
     }
     if(i==2) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=2 is an error, but not fatal"));
     }
  }
}

3
+1 за то, что пользователь заметил предупреждение, а не ошибку. Стоит упомянуть warningsпакет Python , который дает другой шаблон для этой проблемы.
James_pic

Спасибо за отличный ответ! Это больше из того, что я хотел увидеть, дополнительные шаблоны для обработки ошибок, в которых традиционного try / catch может быть недостаточно.
Микайла Маки

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

5

Часто нет ничего плохого в том, чтобы использовать этот шаблон или этот шаблон, если вы используете шаблон, который используют все остальные. В разработке Objective-C наиболее предпочтительным шаблоном является передача указателя, в котором вызываемый метод может поместить объект NSError. Исключения зарезервированы для ошибок программирования и приводят к сбою (если только у вас нет программистов на Java или .NET, пишущих свое первое приложение для iPhone). И это работает довольно хорошо.


4

На вопрос уже дан ответ, но я не могу с собой поделать.

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

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

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

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


Я думаю, что это скорее плохой дизайн. Метод, который принимает аргументы, должен иметь возможность иметь дело с ними или нет - ничего промежуточного. Ваш пример должен иметь Validator(интерфейс), внедренный в рассматриваемый метод (или объект позади него). В зависимости от введенного Validatorметода метод будет продолжаться с неверными паролями - или нет. Окружающий код может затем попытаться выполнить, WeakValidatorесли пользователь запросил его, например, после того, как WeakPasswordExceptionбыл сгенерирован первоначально попытанным StrongValidator.
JHR

Ах, но я не говорил, что это не может быть интерфейс или валидатор. Я даже не упомянул JSR303. И, если вы внимательно прочитаете, я постарался не говорить слабый пароль, скорее, чтобы он был не очень надежным. Слабый пароль был бы причиной, чтобы остановить поток и попросить пользователя более надежный пароль.
Александр Сантос

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

@jhr В валидаторах, которые я написал, я обычно создаю AggregateException(или подобное ValidationException) и помещаю конкретные исключения для каждой проблемы валидации в InnerExceptions. Например, это может быть BadPasswordException: «Пароль пользователя меньше минимальной длины 6» или MandatoryFieldMissingException: «Имя пользователя должно быть предоставлено» и т. Д. Это не эквивалентно кодам ошибок. Все эти сообщения могут отображаться пользователю таким образом, чтобы они его понимали, и если NullReferenceExceptionвместо этого было выброшено a , мы получили ошибку.
Омер Икбал

4

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

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

Другие ответы объясняли, почему исключения должны быть предпочтительнее кодов ошибок в целом.


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

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

1
@jhr: Когда-нибудь можно что-нибудь гарантировать? Контракт для класса может указывать, что у клиентов есть определенные обязанности; если клиенты соблюдают договор, они будут делать то, что требует договор. Если они этого не сделают, любые последствия будут ошибкой клиентского кода. Если кто-то хочет защититься от случайных ошибок клиента и имеет контроль над типом, возвращаемым методом сериализации, он может иметь флаг «неподтвержденное возможное повреждение» и не разрешать клиентам читать данные из него без вызова AcknowledgePossibleCorruptionметода. .
Supercat

1
... но наличие класса объекта для хранения информации о проблемах может быть более полезным, чем создание исключений или возвращение кода ошибки pass-fail. Приложение может использовать эту информацию подходящим способом (например, при загрузке файла «Foo», сообщить пользователю, что данные могут быть ненадежными, и предложить пользователю выбрать новое имя при сохранении).
суперкат

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

2

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

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


Интересный взгляд на вопрос. Я думаю, что проблема с моим вопросом и моим пониманием - неясная терминология. То, что вы описываете, не является исключением, но это ошибка. Как мы должны это назвать?
Микайла Маки

1

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

В Perl 6 это делается через fail, который, если в no fatal;области видимости, возвращает специальный неисключенный Failureобъект исключения .

В Perl 5 вы можете использовать Contextual :: Return, с которым вы можете сделать это return FAIL.


-1

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

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

РЕДАКТИРОВАТЬ: я не понимал, что это вопрос программной парадигмы, а не конкретного случая.

Позвольте мне дополнительно уточнить мои пункты в моем конкретном случае, в котором мой ответ будет иметь смысл

  1. У меня есть коллекции объектов сущностей
  2. У меня есть веб-сервисы процедурного стиля, которые работают с этими объектами сущностей

Есть два вида ошибок:

  1. Ошибки при обработке происходят на уровне сервиса
  2. Ошибки, потому что есть несоответствия в объектах сущности

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

Я думал о втором типе ошибки. И мой ответ об этом втором виде ошибок. В объектах сущностей есть выбор для нас, некоторые из них

  • используя проверочную переменную
  • бросить исключение немедленно, когда поле установлено неправильно из установщика

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

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


Я понимаю, о чем ты говоришь. Интересный подход. Я рад, что это часть набора ответов.
Микайла Маки
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.