валидация входных данных [re: Что значит: "уметь прогать"]
Оцени с позиции красоты мой код:для этого и придумали exception-ы
if (n<0)
throw new ArgumentException("N is negative");
if (n>MaxComputable)
throw new ArgumentException("N is above maximum allowed value");
return n*factorial(n-1);
стало бы красиво? Я так не думаю.
Если код работает предсказуемо и адекватно при невалидных входных данных - это его плюс, но этот плюс не всегда важен.это не плюс - это необходимое условие.
при невалидных данных - код, как минимум, должен вести себя адекватно
например, твой красивый код при передаче -1 вызывает stackoverflow и завершение всего процесса.
но если в нем было бы if (n <= 0) , вместо равенства - то это уже был хороший код
зы
в коде должен отсутствовать эффект дятла: когда залетевший дятел рушит весь город.
твой красивый код - резко усиливает небольшую ошибку в коде, например, вызов factorial(x-1 когда x - это, например, кол-во участников и никто не пришел
стало бы красиво? Я так не думаю.второй exception точно лишний, исключение переполнения должен генерить процессор.
первый exception лучше заменить на доопределение функции factorial на отрицательную плоскость. например, как возвращающую 0
твой красивый код - резко усиливает небольшую ошибку в коде, например, вызов factorial(x-1 когда x - это, например, кол-во участников и никто не пришелПонимаешь, это просто сферический код в вакууме. Ты ведь не знаешь, какую задачу он решает. Тем не менее, у тебя какие-то категорические требования к эффекту дятла.
Про factorial(x-1) - это вообще fail, мой факториал работает нормально с нулевым аргументом. Или у тебя в твоей модели может и отрицательное число участников прийти?
И я там специально написал именно if (n==0 чтобы подчеркнуть мой тезис о том, что СУЩЕСТВУЮТ СЛУЧАИ, когда в коде не следует заботиться о том, как он работает на невалидных данных. Например: олимпиадные задачи. Другой пример: учебные примеры кода.
например, нельзя написать
for (int i = 0; i < factorial(x-1); ++i)
{
}
придется писать
for (int i = 0; i < x < 0)? 0: factorial(x-1; ++i)
{
}
Про factorial(x-1) - это вообще fail, мой факториал работает нормально с нулевым аргументом. Или у тебя в твоей модели может и отрицательное число участников прийти?подумай еще раз. x - это кол-во участников. и что будет если никто не придет?
первый exception лучше заменить на доопределение функции factorial на отрицательную плоскость. например, как возвращающую 0опять таки, когда как. Может, этот ноль потом у тебя выплывет через 10 тысяч строк кода в ошибке деления на ноль? А все потому, что ты где-то по ошибке вычислил факториал от кода ошибки вместо факториала от числа участников?
подумай еще раз. x - это кол-во участников. и что будет если никто не придет?Понял тебя, угу.
Может, этот ноль потом у тебя выплывет через 10 тысяч строк кода в ошибке деления на ноль? А все потому, что ты где-то по ошибке вычислил факториал от кода ошибки вместо факториала от числа участников?так это уже будет проблема того участка кода, который поделил на 0, и именно там эту ошибку надо исправить
Если ты со мной хочешь поспорить, то не приводи мне примеры. Как мы с тобой прекрасно оба знаем, чтобы опровергнуть утверждение 1, тебе нужно доказать, что для ЛЮБОЙ задачи с входными данными надо проверять их валидность.
так это уже будет проблема того участка кода, который поделил на 0, и именно там эту ошибку надо исправитьдавай приведу пример кода, а то ты меня не понял.
int participants = get_participants_count; // эта функция поломалась, и вернула код ошибки -5, вместо кол-ва участников.
// мы забыли проверить код ошибки
int fact = factorial(participants); // тут fact стал равен нулю
// ...
// TEN THOUSAND LINES LATER
// ...
double probability = x / fact; // division by 0
Так где, по-твоему, надо исправлять ошибку?
Так где, по-твоему, надо исправлять ошибку?заменить коды ошибок на исключения
Я цитирую тебя же:
так это уже будет проблема того участка кода, который поделил на 0, и именно там эту ошибку надо исправить
тверждение 1: существуют задачи, в которых проверка валидности входных данных не нужна.у меня другое утверждение, в чуть другой плоскости:
в реальной практике, при отсутствии явных противоположных требований, стоит стараться писать код так, чтобы он уменьшал побочные эффекты от ошибок(невалидных входных данных, некорректного состояния внешней среды и т.д. а не усиливал
ОК, согласен, но как ты это поймешь по делению на 0, случившемуся в последней строке?в ходе code review - обратно оттрассирую выполнение программы, начиная с места возникновения ошибки.
Я знаю три подхода к обработке невалидных входных данных:
1) Вернуть спецзначение, или заполнить спецпеременную. Это и мой код ошибки, и твое доопределение факториала на отрицательную полуось.
2) Бросить исключение.
3) Забить на эту обработку вовсе.
Все три подхода имеют право на существование. Лично я больше фанат 2-го подхода, но лишь потому, что работаю в .NET Framework, который и сам бросает исключения в случае каких-то ошибок. Если бы я работал под никсами, где принято возвращать код ошибки - использовал бы первый подход.
Третий подход дает наиболее лаконичный и выразительный код. Если тебе надо что-то проиллюстрировать, написать какую-то модельную функцию - 3-й подход тебе поможет сделать это быстро и красиво.
Я считаю, что главное - не следует смешивать подходы. Если ты бросаешь исключения - бросай везде, а не доопределяй факториал нулем.
Соответственно, то, что я написал в "красивом" коде if (n==0 а не if (n<=0) - это реализация моего правила. Если я уж решил чихать на невалидные данные (а я решил, ибо не проверяю верхнюю границу то на все их, а не на подмножество.
И в ответ на твое возражение о том, что верхнюю границу за меня проверит среда и бросит исключение - мой ответ: среда среде рознь. Какая-то, может, и не проверит, и не бросит.
Я считаю, что главное - не следует смешивать подходы.какой цели ты хочешь добиться?
сейчас ты фиксируешь форму, без зафиксированной цели.
(Ц1)главная цель кода - обеспечить удобство пользователю
(Ц2)вторичная цель - обеспечить удобство разработки программисту
(Ц3)подцель - для удобства пользователя, код должен быть построен так, чтобы он гасил (а не усиливал) побочные эффекты от ошибок (пользователя, программиста, окружения и т.д.)
для обеспечения Ц3 при условии Ц2:
1. обработку ошибок стоит строить на исключениях,
2. нет необходимости добавлять вспомогательные исключения, если они не сообщают доп. информацию.
при этом считается, что название метода, входные аргументы и цепочку вызовов - где произошла ошибка мы и так знаем
3. стоит договариваться о доопределении функций(кода) так, что если входные невалидные данные можно считать как редуцированный частный случай, то и возвращаться должны данные, которые можно считать за редуцированный частный случай.
0, пустая коллекция, null - это всё есть редуцированные частные случаи (в зависимости, от типа данных)
стоит договариваться о доопределении функций(кода) так, что если входные невалидные данные можно считать как редуцированный частный случай, то и возвращаться должны данные, которые можно считать за редуцированный частный случай.Ну да, если есть у меня соглашение, что отрицательный аргумент - это допустимый аргумент факториала, и факториал отрицательного числа считаем равным нулю, то я так и напишу.
А почему не следует смешивать подходы? Ну если у тебя половина функций при невалидных входных данных бросает исключение, а вторая половина - возвращает какое-то псевдозначение, то существует определенная вероятность, что ты сам забудешь, что как работает, и будешь неправильно эти функции вызывать.
Если же моя цель - написать лаконично рекурсивный факториал, то дополнительная проверка if (n<0) (или твой первый вариант if (n<=0) return 1) только затуманивает смысл функции. Аргумент о том, что "так она будет работать лучше в таких-то приложениях" для меня нисколько не важен, т.к. в приложениях я вообще не буду считать факториал рекурсивно.
все, я потерял интерес к дискуссии. Думаю, этот вопрос следует отнести к вопросам стиля программирования. А стиль у каждого свой, а у меня - самый лучший
А почему не следует смешивать подходы? Ну если у тебя половина функций при невалидных входных данных бросает исключение, а вторая половина - возвращает какое-то псевдозначение, то существует определенная вероятность, что ты сам забудешь, что как работает, и будешь неправильно эти функции вызывать.А что плохого в том, чтобы часть функций бросала исключения, а часть работала в предположении о корректности входных данных?
видимо то, что предположение о корректности входных данных может оказаться неверным. Ваш К.О.
видимо то, что предположение о корректности входных данных может оказаться неверным. Ваш К.О.Ну да. Одна ошибка в коде - и данные уже некорректные. И хорошо бы, чтобы при этом не произошел тотальный капец, а бросился, скажем, какой-нибудь exception.
С другой стороны, в паранойю впадать тоже не следует.
При некоректных данных код должен работать некорректно, это очевидно. Иначе он будет засираться однообразными тупыми проверками, которые к тому же будут глючить при изменении модели данных.
Ну да. Одна ошибка в коде - и данные уже некорректные. И хорошо бы, чтобы при этом не произошел тотальный капец, а бросился, скажем, какой-нибудь exception.Да, отлично. То-то джава программы генерируют тонны эксепшенов, которые никому не интересны и никем не исправляются.
Я не вполне понял, что ты хотел сказать.
видимо то, что предположение о корректности входных данных может оказаться неверным. Ваш К.О.Допустим, данные, полученные от пользователя, прошли полную проверку в части программы, ответственной за взаимодействие с этим самым пользователем. Ты предлагаешь все эти проверки многократно повторять во всех остальных частях программы?
При некоректных данных код должен работать некорректно, это очевидно.Т.е. если при введении некорректных данных пользователь, например, получает полный доступ к используемой БД, то все норм?
При некоректных данных код должен работать некорректно, это очевидно.Под словом данные здесь понимается не то, что ввел пользователь, а то, что дано на вход функции.
Функции, работающие с вводом пользователя, должны работать корректно с любым возможным вводом. Т.е. для функций UI любые данные в этом смысле корректные.
Допустим, данные, полученные от пользователя, прошли полную проверку в части программы, ответственной за взаимодействие с этим самым пользователем. Ты предлагаешь все эти проверки многократно повторять во всех остальных частях программы?Ну ты как будто не допускал никогда ошибок.
Тут валидация понимается не как фильтрация пользовательского ввода от мусора, а как sanity check параметров вызова внутренних интерфейсов.
Каждый раз,
Я не вполне понял, что ты хотел сказать.Джава программы часто высирают кучи непонятных исключений, при этом продолжают работать без видимых последствий. Явно разработчики просто забивают на анализ и исправление, типа и так работает.
Т.е. если при введении некорректных данных пользователь, например, получает полный доступ к используемой БД, то все норм?Без экстрима, конечно. Кое-где данные нужно проверять, но в каждой функции делать, это уже перебор.
я не понял тогда (и сейчас какой ты делаешь вывод из этого?
Что лучше пиздец, его исправят, а на эксепшен забьют.
так ведь работает же? пусть лучше работает, чем не работает
Что лучше пиздец, его исправят,может исправят, а может добавят костыль, который перезапустит что-нибудь
стало бы красиво? Я так не думаю.А вообще, не пофиг ли на красоты? Вы на своих красотах окончательно повернулись.
Пример про факториал лажовый. И вот почему.
Проверка входных условий должна делаться или не делаться не от желаний разработчика, а согласно декларируемым предусловиям. Если программист в целях повышения производительности не делает никаких проверок — это его право, должно быть только это как-то оговорено.
Что касается самой программы, то она должна вести себя корректно всегда. При любых данных. Молчать программа имеет право только если она корректно отработала, а при каких-то ошибках должна быть внятная диагностика. Внятная — это значит, что тот, кто запускал сразу поймёт где ошибка.
Все кто пишут проги с плохой диагностикой — пидорасы.
Объясни, пожалуйста, как соотносятся два твоих тезиса:
1. Если программист в целях повышения производительности не делает никаких проверок — это его право, должно быть только это как-то оговорено.
2. Что касается самой программы, то она должна вести себя корректно всегда. При любых данных.
Что-то читаю, читаю, а понять не могу. То есть типа программист, если не хочет, может и не делать проверок, но программа все равно должна выдавать хорошую диагностику?..
А вообще, не пофиг ли на красоты?Как это в духе форумлокала! Ты зашел в тему, где обсуждается, какой код красив, а какой нет, для того, чтобы заявить, что тебе плевать на красоту? Так зачем заходил?
1. Если программист в целях повышения производительности не делает никаких проверок — это его право, должно быть только это как-то оговорено.пример: в функции strlen не делать проверки на нулевой указатель. Каждый проверяет сам, чтобы не было лишних проверок, которые могут много стоить. Например, vector в STL не проверяет выход за размеры, и это правильно: почему я должен терять скорость из-за Пупкина, которому джанная фича нужна?
2. Что касается самой программы, то она должна вести себя корректно всегда. При любых данных.
Программа же не должна падать на некорректных данных ни при каких условиях. Особенно если она отвечает удалённо. Хотя в то же самое время допустимо иногда грубо отвечать посылом нах без уточнений, но только в случае если кэп может ответить за вас.
О плохой и хорошей диагностике написано море. У большинства программ, которые пишут хомячки, диагностика ужасная.
Если область применения этого класса неограничена, например, может быть использована в других проектах, другими людьми, то отношусь к нему по всей строгости закона, проверяю юнит-тестами на всевозможных самых хитрожопых невалидных данных.
Если область применения заведомо ограничена, например, функция вызывается только в текущем проекте при известных условиях, то проверки не ставлю.
Оставить комментарий
zorin29
Я считаю, что ты неправ. По-моему, "работает правильно" - это "дает правильный ответ при всех валидных входных данных".Если код работает предсказуемо и адекватно при невалидных входных данных - это его плюс, но этот плюс не всегда важен.
Оцени с позиции красоты мой код:
Лично мне кажется, что он нисколько не красив. А вот этот код, по-моему, красив: