Я ищу рекомендацию здесь. Я борюсь с тем, лучше ли возвращать NULL или пустое значение из метода, когда возвращаемое значение отсутствует или не может быть определено.
Возьмите следующие два метода в качестве примера:
string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID) // finds a Person with a matching personID.
В ReverseString()
, я бы сказал, вернуть пустую строку, потому что тип возвращаемого значения является строка, поэтому вызывающая сторона ожидает этого. Кроме того, таким образом, вызывающая сторона не должна проверять, был ли возвращен NULL.
В FindPerson()
, возвращая NULL кажется лучшей подгонки. Независимо от того, new Person()
возвращен ли NULL или пустой объект Person ( ), вызывающая сторона должна будет проверить, является ли объект Person пустым или пустым, прежде чем что-либо с ним делать (например, вызывать UpdateName()
). Так почему бы просто не вернуть NULL здесь, и тогда вызывающая сторона должна только проверить NULL.
Кто-нибудь еще борется с этим? Любая помощь или понимание приветствуется.
Ответы:
StackOverflow хорошо обсуждает эту тему в этом разделе вопросов и ответов . В вопросе с самым высоким рейтингом kronoz отмечает:
Лично мне нравится возвращать пустые строки для функций, которые возвращают строки, чтобы минимизировать объем обработки ошибок, который необходимо ввести в действие. Однако вам необходимо убедиться, что группа, с которой вы работаете, будет придерживаться того же соглашения, иначе преимущества этого решения не будут достигнуты.
Однако, как отмечается в ответе SO, ноль, вероятно, должен быть возвращен, если ожидается объект, чтобы не было сомнений в том, возвращаются ли данные.
В конце концов, нет лучшего способа сделать что-либо. Построение консенсуса в команде, в конечном итоге, послужит основой для лучших практик вашей команды.
источник
for x in list_of_things() {...}
тогда, возможно, быстрее, чтобы впасть в грохотl = list_of_things(); if l != null {...}
emit(user.hometown)
чемif user.hometown == null { emit("") else {emit(user.hometown)}
Во всем коде, который я пишу, я избегаю возврата
null
из функции. Я прочитал это в Чистом Коде .Проблема с использованием
null
состоит в том, что человек, использующий интерфейс, не знает,null
является ли возможный результат, и должны ли они проверять это, потому что нетnot null
ссылочного типа.В F # вы можете вернуть
option
тип, который может бытьsome(Person)
илиnone
, так что для вызывающей стороны очевидно, что они должны проверить.Аналогичным C # (анти) паттерном является
Try...
метод:Теперь я знаю , что люди говорят , что они ненавидят в
Try...
шаблон , потому имеющие выходной параметр разбивают идеи чистой функции, но это на самом деле не отличается:... и, честно говоря, вы можете предположить, что каждый программист .NET знает о
Try...
шаблоне, потому что он используется внутри .NET Framework. Это означает, что им не нужно читать документацию, чтобы понять, что она делает, что для меня важнее, чем придерживаться какого-то пуристского взгляда на функции (понимание, чтоresult
этоout
параметр, а неref
параметр).Так что я бы пошел,
TryFindPerson
потому что вы, кажется, указываете, что совершенно нормально быть не в состоянии найти это.Если, с другой стороны, нет логической причины, по которой вызывающая сторона когда-либо предоставит
personId
несуществующий объект , я, вероятно, сделал бы это... а потом я бы бросил исключение, если оно было недействительным.
Get...
Префикс означает , что абонент знает , что это удастся.источник
Вы можете попробовать шаблон Специального случая Мартина Фаулера из Paterns of Enterprise Application Architecture :
источник
Я бы подумал,
ReverseString()
что вернул бы обратную строку, иIllegalArgumentException
если бы передал вNull
.Я думаю, что
FindPerson()
следует следовать шаблону NullObject или выдвигать неконтролируемое исключение, касающееся отсутствия чего-либо, если вы всегда сможете найти что-то.Null
Нужно иметь дело с тем , чего следует избегать. ИмеяNull
в языках был назван миллиард долларов Ошибка по его изобретатель!источник
Вернуть опцию . Все преимущества возврата другого недопустимого значения (например, возможность иметь пустые значения в ваших коллекциях) без какого-либо риска NullPointerException.
источник
boost::optional<T>
Я вижу обе стороны этого аргумента, и я понимаю, что некоторые довольно влиятельные голоса (например, Фаулер) выступают за то, чтобы не возвращать нули, чтобы сохранить код чистым, избежать дополнительных блоков обработки ошибок и т. Д.
Тем не менее, я склонен на сторону сторонников возврата нуля. Я нахожу, что есть важное различие в вызове метода, и он отвечает " У меня нет данных", и он отвечает "У меня есть эта пустая строка" .
Поскольку я видел некоторые обсуждения, относящиеся к классу Person, рассмотрим сценарий, в котором вы пытаетесь найти экземпляр класса. Если вы передадите какой-нибудь атрибут поиска (например, идентификатор), клиент может немедленно проверить наличие нуля, чтобы увидеть, не найдено ли значение. Это не обязательно исключение (следовательно, не требует исключений), но оно также должно быть четко задокументировано. Да, это требует некоторой строгости со стороны клиента, и нет, я не думаю, что это вообще плохо.
Теперь рассмотрим альтернативу, где вы возвращаете действительный объект Person ... в котором ничего нет. Вы ставите пустые значения во все его значения (имя, адрес, FavoritesDrink), или вы теперь наполняете их действительными, но пустыми объектами? Как ваш клиент теперь может определить, что фактическое лицо не было найдено? Нужно ли им проверять, является ли имя пустой строкой вместо нуля? Разве такого рода вещи не приведут к большому или большему количеству беспорядка в коде и условных операторов, чем если бы мы только что проверили нулевое значение и пошли дальше?
Опять же, по обе стороны этого аргумента есть пункты, с которыми я мог бы согласиться, но я считаю, что это имеет наибольшее значение для большинства людей (делая код более понятным).
источник
FindPerson
илиGetPerson
возвращает ноль или выбрасывает исключение, если ключ не существует?» Как пользователь API я просто не знаю, и я должен проверить документацию. С другой стороны,bool TryGetPerson(Guid key, out Person person)
не требует, чтобы я проверял документацию, и я знаю, что исключение не выдается в том, что составляет довольно не исключительное обстоятельство. Это то, что я пытаюсь сделать в своем ответе.findPerson
), абсолютно нормально, чтобы не было никакого результата. Это не должно приводить к исключению.Верните ноль, если вам нужно знать, существует элемент или нет. В противном случае верните ожидаемый тип данных. Это особенно верно, если вы возвращаете список предметов. Обычно можно предположить, что если вызывающий абонент хочет получить список, он захочет перебрать список. Многие (большинство? Все?) Языки терпят неудачу, если вы пытаетесь перебрать ноль, но не когда вы перебираете пустой список.
Я расстраиваюсь, пытаясь использовать код, подобный этому, только для того, чтобы он потерпел неудачу:
Мне не нужно было добавлять особый случай для случая, когда нет вещей .
источник
Это зависит от семантики метода. Вы можете указать, что ваш метод принимает только «Не ноль». В Java вы можете объявить это, используя некоторые аннотации метаданных:
Вы должны как-то указать возвращаемое значение. Если вы объявляете, вы принимаете только значения NotNull, вы обязаны вернуть пустую строку (если ввод был также пустой строкой).
Второй случай немного сложен по своей семантике. Если этот метод предназначен для возврата какого-либо человека по его первичному ключу (и ожидается, что этот человек существует), лучшим способом является создание исключения.
Если вы ищете человека по какому-то угаданному идентификатору (что мне кажется странным), вам лучше объявить этот метод как @Nullable.
источник
Я бы порекомендовал использовать шаблон Null Object всякий раз, когда это возможно. Это упрощает код при вызове ваших методов, и вы не можете читать ужасный код, такой как
if (someObject != null && someObject.someMethod () != whateverValue)
Например, если метод возвращает коллекцию объектов, которые соответствуют некоторому шаблону, то возвращение пустой коллекции имеет больше смысла, чем возврат
null
, и итерация по этой пустой коллекции практически не приведет к снижению производительности. Другим случаем может быть метод, который возвращает экземпляр класса, используемый для регистрации данных, возвращая объект Null, а неnull
является предпочтительным, на мой взгляд, так как он не заставляет пользователей всегда проверять, является ли возвращенная ссылкаnull
.В тех случаях, когда возврат
null
имеет смысл (например, вызываяfindPerson ()
метод), я бы попытался, по крайней мере, предоставить метод, который возвращает, если объект присутствует (personExists (int personId)
например), другим примером может бытьcontainsKey ()
методMap
в Java. Это делает код вызывающего абонента чище, так как вы можете легко увидеть, что существует вероятность того, что требуемый объект может быть недоступен (человек не существует, ключ отсутствует на карте). Постоянная проверка, не ссылается ли ссылкаnull
на код, по моему мнению.источник
Нуль лучше всего вернуть, если и только если применяются следующие условия:
Кроме того, убедитесь, что вы задокументировали тот факт, что функция может возвращать ноль.
Если вы следуете этим правилам, нули в основном безвредны. Обратите внимание, что если вы забудете проверить нулевое значение, вы сразу же получите исключение NullPointerException, которое обычно довольно легко исправить. Этот быстрый подход к сбоям намного лучше, чем наличие поддельного возвращаемого значения (например, пустой строки), которое спокойно распространяется по вашей системе, возможно, портит данные, не вызывая исключения.
Наконец, если вы примените эти правила к двум функциям, перечисленным в вопросе:
источник
По моему мнению, есть разница между возвратом NULL, возвратом некоторого пустого результата (например, пустой строки или пустого списка) и выдачей исключения.
Я обычно придерживаюсь следующего подхода. Я рассматриваю вызов функции или метода f (v1, ..., vn) как применение функции
где S это «состояние мира» T1, ..., Tn - типы входных параметров, а T - тип возвращаемого значения.
Сначала я попытаюсь определить эту функцию. Если функция частичная (то есть есть некоторые входные значения, для которых она не определена), я возвращаю NULL, чтобы сигнализировать об этом. Это потому, что я хочу, чтобы вычисление завершалось нормально и сообщало мне, что функция, которую я запросил, не определена для заданных входных данных. Использование, например, пустой строки в качестве возвращаемого значения неоднозначно, поскольку может быть так, что функция определена на входах, а пустая строка является правильным результатом.
Я думаю, что дополнительная проверка для указателя NULL в вызывающем коде необходима, потому что вы применяете частичную функцию, и задача вызываемого метода - сообщить вам, если функция не определена для данного ввода.
Я предпочитаю использовать исключения для ошибок, которые не позволяют выполнять вычисления (т.е. не было возможности найти какой-либо ответ).
Например, предположим, что у меня есть класс Customer, и я хочу реализовать метод
искать клиента в базе данных приложения по его коду. В этом методе я бы
Дополнительные проверки для нуля, например
являются частью семантики того, что я делаю, и я бы не просто «пропустил их», чтобы код читался лучше. Я не думаю, что хорошей практикой является упрощение семантики рассматриваемой проблемы просто для упрощения кода.
Конечно, поскольку проверка на ноль происходит очень часто, хорошо, если язык поддерживает какой-то специальный синтаксис для нее.
Я также хотел бы рассмотреть возможность использования шаблона Null Object (как предложено Laf), пока я могу отличить нулевой объект класса от всех других объектов.
источник
Методы, возвращающие коллекции, должны возвращать пустые, но другие могут возвращать ноль, потому что для коллекций может случиться так, что у вас будет объект, но в нем нет элементов, поэтому вызывающая программа будет проверять только размер, а не оба.
источник
Для меня здесь есть два случая. Если вы возвращаете какой-то список, вы всегда должны возвращать список, несмотря ни на что, пустой, если вам нечего в него вставить.
Единственный случай, когда я вижу какую-либо дискуссию, это когда вы возвращаете один предмет. Я предпочитаю возвращать null в случае неудачи в такой ситуации, потому что все происходит быстро.
Если вы возвращаете нулевой объект, и вызывающему объекту нужен реальный объект, он может пойти дальше и попытаться использовать нулевой объект, что приведет к неожиданному поведению. Я думаю, что для рутины будет лучше, если они забудут разобраться с ситуацией. Если что-то не так, я хочу исключение как можно скорее. Вы вряд ли отправите ошибку таким образом.
источник
Добавим к тому, что люди уже сказали о
Maybe
конструкторе типов:Другим большим преимуществом, кроме того, чтобы не рисковать NPE, является семантический. В функциональном мире, где мы имеем дело с чистыми функциями, нам нравится пытаться создавать функции, которые при применении в точности эквивалентны их возвращаемому значению . Рассмотрим случай
String.reverse()
: это функция, которая с определенной строкой представляет обратную версию этой строки. Мы знаем, что эта строка существует, потому что каждая строка может быть обращена (строки - это в основном упорядоченные наборы символов).Теперь о чем
findCustomer(int id)
? Эта функция представляет клиента с указанным идентификатором. Это то, что может или не может существовать. Но помните, функции имеют одно возвращаемое значение, поэтому вы не можете сказать «эта функция возвращает клиента с заданным идентификатором ИЛИ возвращаетnull
.Это моя главная проблема как с возвратом, так
null
и с возвратом нулевого объекта. Они вводят в заблуждение.null
не является клиентом, и на самом деле это не "отсутствие клиента". Это отсутствие НИЧЕГО. Это просто указатель на НИЧЕГО. Очень низкоуровневый, очень уродливый и очень подверженный ошибкам. Я думаю, что нулевой объект также вводит в заблуждение. Нулевой клиент - это клиент. Это не отсутствие, это не клиент с запрашиваемым идентификатором, поэтому возвращать его просто НЕПРАВИЛЬНО. Это НЕ тот клиент, о котором я просил, но вы утверждаете, что вернули клиента. У него неправильный идентификатор, очевидно, это ошибка. Это нарушает договор, предложенный именем метода и подписью. Требуется, чтобы клиентский код предполагал что-то о вашем дизайне или читал документацию.Обе эти проблемы решаются с помощью
Maybe Customer
. Неожиданно мы не говорим «эта функция представляет клиента с заданным идентификатором». Мы говорим: «Эта функция представляет клиента с заданным идентификатором, если он существует . Теперь он очень четко и ясно рассказывает о том, что он делает. Если вы попросите клиента с несуществующим идентификатором, вы получитеNothing
. Как это лучше? чемnull
? Не только для риска NPE. Но также и потому, что типNothing
не простоMaybe
. ЭтоMaybe Customer
. Он имеет семантическое значение. Он, в частности, означает «отсутствие клиента», указывая на то, что такого клиента не существует.Другая проблема с нулем, конечно, в том, что он неоднозначен. Это вы не нашли клиента или произошла ошибка соединения с БД, или мне просто не разрешили увидеть этого клиента?
Если у вас есть несколько таких ошибок, как эта, вы можете либо генерировать исключения для этих версий, либо (и я предпочитаю таким образом) вы можете возвращать
Either CustomerLoadError Customer
, представляя либо что-то не так, либо возвращенного клиента. Он имеет все преимущества,Maybe Customer
а также позволяет указать, что может пойти не так.Главное, что мне нужно, это зафиксировать контракт функции в его подписи. Не принимайте вещи, не полагайтесь на мимолетные соглашения, будьте явными.
источник
1) Если семантика функции заключается в том, что она ничего не может вернуть, вызывающая программа ДОЛЖНА проверить это, в противном случае он будет, например. возьми свои деньги и никому их не отдай.
2) Хорошо делать функции, которые семантически всегда что-то возвращают (или бросают).
При использовании логгера имеет смысл возвращать логгер, который не регистрирует, если логгер не был определен. При возврате коллекции почти никогда не имеет смысла (кроме как на «достаточно низком уровне», где сама коллекция является данными, а не тем, что она содержит), чтобы ничего не возвращать, потому что пустой набор - это набор, а не ничто.
В случае с личным примером я бы пошел «спящим» способом (получить против нагрузки, IIRC, но там это усложняется прокси-объектами для отложенной загрузки) с двумя функциями: одна возвращает ноль, а вторая - выбрасывает.
источник
NULL должен быть возвращен, если приложение ожидает, что данные будут доступны, но данные недоступны. Например, сервис, который возвращает CITY на основе почтового индекса, должен возвращать ноль, если город не найден. Затем вызывающий абонент может решить обработать нулевое значение или взорвать.
Пустой список должен быть возвращен, если есть две возможности. Данные доступны или данных нет. Например, сервис, который возвращает CITY (s), если численность населения превышает определенное число. Может вернуть пустой список, если нет данных, удовлетворяющих заданным критериям.
источник
Возвращение NULL - ужасный дизайн в объектно-ориентированном мире. Короче говоря, использование NULL приводит к:
Проверьте это сообщение в блоге для подробного объяснения: http://www.yegor256.com/2014/05/13/why-null-is-bad.html
источник