Должны ли мы разрешить нулевые / пустые параметры? - PullRequest
10 голосов
/ 22 сентября 2010

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

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

Давайте рассмотрим два конкретных примера:

1) Учитывая, что у нас есть класс Interval с методом overlaps (Interval), что следует сделатьпроизойдет, если в качестве параметра будет передано значение null?У меня такое чувство, что мы должны выбросить исключение IllegalArgumentException, чтобы звонящий узнал, что, возможно, что-то не так, но мой коллега считает, что возвращать false достаточно, поскольку в сценариях, где он его использует, просто не имеет значения, если второй интервалnull или нет (все, что имеет значение - перекрываются ли они).

2) При наличии такого метода, как fetchByIds (идентификаторы коллекций), что должно произойти, если предоставляется пустая коллекция?Я хотел бы еще раз предупредить вызывающего абонента о том, что происходит что-то ненормальное, но мой коллега может просто получить пустой список, так как еще раз его не волнует, есть ли какие-либо идентификаторы или нет.

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

РЕДАКТИРОВАТЬ: я вижу много хороших ответов, и большинство склонныскажем, определите это как контракт / в документации и придерживайтесь его, но я хотел бы, чтобы ваше мнение о том, когда это разрешать, а когда нет (если вообще).В конкретных примерах, что бы вы сделали?Принимая во внимание, что для 90% использований без проверки входных данных все будет в порядке, вы все равно будете проверять, чтобы избавиться от ошибок в оставшихся 10%, или вы предпочитаете устранять их по мере их появления и избегать ненужных пустых / пустых проверок?

Ответы [ 5 ]

6 голосов
/ 24 сентября 2010

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

1) Учитывая, что у нас есть класс Interval с методом overlaps (Interval), что должно произойти, если передан нолькак параметр?У меня такое чувство, что мы должны выбросить исключение IllegalArgumentException, чтобы вызывающий абонент узнал, что, возможно, что-то не так, но мой коллега считает, что возвращать false достаточно ...

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

2)При наличии такого метода, как fetchByIds (идентификаторы коллекций), что должно произойти, если будет предоставлена ​​пустая коллекция?

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

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

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

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

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

4 голосов
/ 22 сентября 2010

Основным недостатком многочисленных основных языков, включая Java, C # и большинство языков сценариев, является невозможность сказать: «Здесь нужно передать значение!»Я смутно припоминаю интервью с Хейлсбергом, в котором он даже признал, что это было одной вещью, о которой он сожалел о дизайне C # (исключение постоянства - это еще одно бедствие, ИМО, но не будем отвлекаться).Мазь - это причина, по которой вы и ваш коллега так обеспокоены этим вопросом.Для этого нет решения (кроме случаев, когда разработчики языка могут каким-то образом модифицировать исправление).Любой выбор предполагает компромисс.Как только вы оба поймете это, тем скорее вы сможете расслабиться и просто выбрать то, что вызывает наименьшее количество боли.

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

3 голосов
/ 22 сентября 2010

Мой общий подход - не принимать ноль.Пустая коллекция - это хорошо.

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

3 голосов
/ 22 сентября 2010

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

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

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

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

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

2 голосов
/ 22 сентября 2010

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

Например, если у вас есть метод с именем PrintList (), и у вас есть аргумент, который может быть нулевым, несет ли этот метод ответственность за получение списка, а затем за его печать или только за печать? Может быть, имя метода следует изменить, чтобы включить эту ответственность: GetAndPrintList ().

Также спросите себя, что произойдет, если в команду добавится новый участник: найдет ли он код, который легко понять и прочитать без дополнительной документации?

...