Я долго думал об этой проблеме, и мне было бы интересно узнать мнение других разработчиков.
Я склонен придерживаться очень оборонительного стиля программирования. Мой типичный блок или метод выглядит так:
T foo(par1, par2, par3, ...)
{
// Check that all parameters are correct, return undefined (null)
// or throw exception if this is not the case.
// Compute and (possibly) return result.
}
Кроме того, во время вычислений я проверяю все указатели перед разыменованием их. Моя идея состоит в том, что, если есть какая-то ошибка и где-то должен появиться указатель NULL, моя программа должна хорошо это обработать и просто отказаться от продолжения вычислений. Конечно, он может уведомить о проблеме с сообщением об ошибке в журнале или каким-либо другим механизмом.
Чтобы выразить это более абстрактно, мой подход
if all input is OK --> compute result
else --> do not compute result, notify problem
Другие разработчики, в том числе некоторые мои коллеги, используют другую стратегию. Например, они не проверяют указатели. Они предполагают, что часть кода должна быть правильно введена, и она не должна нести ответственность за то, что происходит, если ввод неправильный. Кроме того, если исключение NULL-указателя приводит к сбою программы, ошибка будет обнаружена легче во время тестирования и у нее больше шансов быть исправленной.
Мой ответ на это обычно: но что, если ошибка не обнаружена во время тестирования и появляется, когда продукт уже используется клиентом? Каков предпочтительный способ проявления ошибки? Должна ли это быть программа, которая не выполняет определенного действия, но все еще может продолжать работать, или программа, которая аварийно завершает работу и требует перезапуска?
Подведение итогов
Какой из двух подходов к обработке неправильного ввода вы бы посоветовали?
Inconsistent input --> no action + notification
или
Inconsistent input --> undefined behaviour or crash
редактировать
Спасибо за ответы и предложения. Я тоже фанат дизайна по контракту. Но даже если я доверяю человеку, который написал код, вызывающий мои методы (возможно, это я), все равно могут быть ошибки, приводящие к неправильному вводу. Поэтому мой подход заключается в том, чтобы никогда не предполагать, что метод передан корректно.
Кроме того, я бы использовал механизм, чтобы поймать проблему и уведомить об этом. В системе разработки, например, откроется диалоговое окно для уведомления пользователя. В производственной системе он просто записывает некоторую информацию в журнал. Я не думаю, что дополнительные проверки могут привести к проблемам с производительностью. Я не уверен, достаточно ли утверждений, если они отключены в производственной системе: возможно, возникнет какая-то ситуация в производстве, которая не возникла во время тестирования.
В любом случае, я был очень удивлен, что многие люди придерживаются противоположного подхода: они позволяют приложениям аварийно завершать работу «нарочно», поскольку утверждают, что это облегчит поиск ошибок во время тестирования.
источник
Ответы:
Вы правильно поняли. Будь параноиком. Не доверяйте другому коду, даже если это ваш собственный код. Вы забываете вещи, вы вносите изменения, код развивается. Не верь внешнему коду.
Выше было высказано хорошее замечание: что делать, если входные данные неверны, но программа не падает? Затем вы получаете мусор в базе данных и ошибки по линии.
Когда спрашивают номер (например, цену в долларах или количество единиц), я люблю вводить «1e9» и посмотреть, что делает код. Это может случится.
Четыре десятилетия назад, получив степень бакалавра в области компьютерных наук от UCBerkeley, нам сказали, что хорошая программа - это 50% обработка ошибок. Будь параноиком.
источник
У вас уже есть правильная идея
Какой из двух подходов к обработке неправильного ввода вы бы посоветовали?
или лучше
Вы не можете по-настоящему использовать подход к работе с печеньем (вы могли бы), но в конечном итоге вы получите шаблонный дизайн, который делает вещи по привычке, а не по осознанному выбору.
Сдержанность догматизма с прагматизмом.
Стив Макконнелл сказал это лучше всего
Стив Макконнелл в значительной степени написал книгу ( Code Complete ) по защитному программированию, и это был один из методов, которые он посоветовал вам всегда проверять на правильность своих данных.
Я не помню, упоминал ли Стив об этом, однако вам следует подумать о том, чтобы сделать это для не приватных методов и функций и только для тех, где это необходимо.
источник
Здесь нет «правильного» ответа, особенно без указания языка, типа кода и типа продукта, в который может войти код. Рассмотреть возможность:
Язык имеет значение. В Objective-C часто нормально отправлять сообщения на ноль; ничего не происходит, но программа также не падает. Java не имеет явных указателей, поэтому нулевые указатели не представляют большой проблемы. В C вам нужно быть немного более осторожным.
Быть параноиком означает необоснованное, необоснованное подозрение или недоверие. Это, вероятно, не лучше для программного обеспечения, чем для людей.
Ваш уровень беспокойства должен быть соизмерим с уровнем риска в коде и вероятной трудностью выявления любых проблем, которые действительно обнаруживаются. Что происходит в худшем случае? Пользователь перезапускает программу и продолжает, где они остановились? Компания теряет миллионы долларов?
Вы не можете всегда идентифицировать плохой ввод. Вы можете неукоснительно сравнивать ваши указатели с нулем, но это только один из 2 ^ 32 возможных значений, почти все из которых являются плохими.
Существует множество различных механизмов для устранения ошибок. Опять же, это зависит в некоторой степени от языка. Вы можете использовать макрос assert, условные операторы, модульные тесты, обработку исключений, тщательный дизайн и другие методы. Ни один из них не является надежным, и ни один не подходит для любой ситуации.
Таким образом, это в основном сводится к тому, где вы хотите поставить ответственность. Если вы пишете библиотеку для использования другими людьми, вы, вероятно, хотите быть настолько осторожными, насколько это разумно, в отношении получаемых вами данных и прилагать все усилия, чтобы по возможности выдавать полезные ошибки. В ваших собственных частных функциях и методах вы можете использовать утверждения для выявления глупых ошибок, но в противном случае возьмете на себя ответственность за вызов (не за вас), чтобы не пропускать мусор.
источник
Там обязательно должно быть уведомление, например, выброшенное исключение. Он служит предупреждением другим кодировщикам, которые могут неправильно использовать написанный вами код (пытаясь использовать его для чего-то, что не было предназначено), что их ввод неверен или приводит к ошибкам. Это очень полезно для отслеживания ошибок, в то время как если вы просто вернете null, их код будет продолжаться, пока они не попытаются использовать результат и получить исключение из другого кода.
Если ваш код сталкивается с ошибкой во время вызова какого-либо другого кода (возможно, неудачного обновления базы данных), который выходит за рамки этого конкретного фрагмента кода, вы действительно не можете его контролировать, и единственный выход - бросить исключение, объясняющее, что вы знаете (только то, что вам говорит код, который вы назвали). Если вы знаете, что определенные входные данные неизбежно приведут к такому результату, вы можете просто не беспокоиться о выполнении своего кода и выдать исключение, указывающее, какой ввод недопустим и почему.
На более заметке, связанной с конечным пользователем, лучше вернуть что-то описательное, но простое, чтобы каждый мог понять это. Если ваш клиент звонит и говорит: «Сбой программы, исправьте ее», у вас есть много работы, чтобы отследить, что пошло не так и почему, и надеясь, что вы сможете воспроизвести проблему. Использование правильной обработки исключений может не только предотвратить сбой, но и предоставить ценную информацию. Вызов от клиента, говорящий: «Программа выдает мне ошибку. Она говорит:« XYZ не является допустимым вводом для метода M, потому что Z слишком большой », или что-то в этом роде, даже если они понятия не имеют, что это значит, вы точно знать, где искать. Кроме того, в зависимости от методов ведения бизнеса вашей / вашей компании, возможно, вы не решаете эти проблемы, поэтому лучше оставить им хорошую карту.
Итак, короткая версия моего ответа такова: ваш первый вариант - лучший.
источник
Я боролся с этой же проблемой во время прохождения университетского курса по программированию. Я наклонился к параноидальной стороне и, как правило, все проверял, но мне сказали, что это неправильное поведение.
Нас учили «Дизайн по контракту». Подчеркивается, что предварительные условия, инварианты и постусловия должны быть указаны в комментариях и проектных документах. Как человек, реализующий мою часть кода, я должен доверять архитектору программного обеспечения и расширять его возможности, следуя спецификациям, которые будут включать предварительные условия (какие входные данные должны обрабатывать мои методы и какие входные данные я не буду отправлять) , Чрезмерная проверка при каждом вызове метода приводит к завороту.
Утверждения следует использовать во время итераций сборки для проверки правильности программы (проверка предварительных условий, инвариантов, постусловий). Утверждения будут затем отключены в производственной компиляции.
источник
Использование «утверждений» - это способ уведомить коллег-разработчиков о том, что они делают это неправильно, только в «частных» методах . Включение / отключение их - это всего лишь один флаг, который можно добавлять / удалять во время компиляции, и поэтому легко удалить утверждения из производственного кода. Есть также отличный инструмент, чтобы узнать, делаете ли вы что-то неправильно в своих методах.
Что касается проверки входных параметров в открытых / защищенных методах, я предпочитаю работать с защитой, проверять параметры и выбрасывать InvalidArgumentException или тому подобное. Вот почему здесь есть для. Это также зависит от того, пишете ли вы API или нет. Если это API, и даже больше, если это закрытый исходный код, лучше проверить все, чтобы разработчики точно знали, что пошло не так. В противном случае, если источник доступен другим разработчикам, он не является черно-белым. Просто будьте согласны с вашим выбором.
Отредактируйте: просто чтобы добавить, что если вы посмотрите, например, на Oracle JDK, вы увидите, что они никогда не проверяют наличие «null» и позволяют сбою кода. Так как это все равно будет выдавать исключение NullPointerException, зачем беспокоиться о проверке на наличие нуля и создании явного исключения. Я думаю, в этом есть какой-то смысл.
источник