Возвращает null плохой дизайн?

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

псевдокод:

variable x = object.method()
if (x is null) do something

24 ответов


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

например, если бы я должен был определить метод в Java, который вернул коллекцию, я обычно предпочел бы вернуть пустую коллекцию (т. е. Collections.emptyList()), а не null, как это означает мой клиентский код чище; например

Collection<? extends Item> c = getItems(); // Will never return null.

for (Item item : c) { // Will not enter the loop if c is empty.
  // Process item.
}

... что чище, чем:

Collection<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}

хорошее использование возврата null:

  • Если null является допустимым функциональное результат, например: FindFirstObjectThatNeedsProcessing () может возвращать null, если не найден, и вызывающий должен проверить соответствующим образом.

плохое использование: попытка заменить или скрыть исключительные ситуации, такие как:

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

в этих случаях исключение более адекватно, так как:

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

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

  • недопустимое имя пользователя / пароль (или любые пользовательские входы)
  • разрыв петель или как нелокальные gotos

да, возвращая NULL является ужасный дизайн, в объектно-ориентированном мире. В двух словах, нулевое использование приводит к:

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

проверьте это сообщение в блоге для подробного объяснения: http://www.yegor256.com/2014/05/13/why-null-is-bad.html. Подробнее в моей книге Элегантный Объекты раздел 4.1.


кто сказал, что это плохой дизайн?

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


основываясь на том, что вы сказали до сих пор, я думаю, что информации недостаточно.

возврат null из метода CreateWidget () кажется плохим.

возврат null из метода FindFooInBar () кажется прекрасным.


его изобретатель говорит это ошибка на миллиард долларов!


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


Если Вы читаете все ответы, становится ясно, что ответ на этот вопрос зависит от вида метода.

во-первых, когда происходит что-то исключительное (IOproblem и т. д.), логически создаются исключения. Когда именно что-то исключительное, вероятно, что-то для другой темы..

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

  • если можно вернуть нейтральное значение, do так что.
    Пустые enumrables, строк и т. д. являются хорошими примерами
  • если такого нейтрального значения не существует,null должен быть возвращен.
    Как уже упоминалось, метод предполагается, возможно, не имеет результата, поэтому он не является исключительным, следовательно, не должен вызывать исключение. Нейтральное значение невозможно (например: 0 не является особенно нейтральным результатом, в зависимости от программы)

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

Я еще не совсем в порядке с именем, но не мог придумать ничего лучше. Так что пока я этим займусь.


это нормально, чтобы вернуть null, если это имеет смысл в некотором роде:

public String getEmployeeName(int id){ ..}

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

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

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}

исключение исключениеal обстоятельства.

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


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

Если результатом метода является то, что не будет иметь хорошего результата при нормальном использовании, возврат null прекрасен:

object x = GetObjectFromCache();   // return null if it's not in the cache

Если действительно всегда должен быть ненулевой результат, то, возможно, лучше выбросить исключение:

try {
   Controller c = GetController();    // the controller object is central to 
                                      //   the application. If we don't get one, 
                                      //   we're fubar

   // it's likely that it's OK to not have the try/catch since you won't 
   // be able to really handle the problem here
}
catch /* ... */ {
}

у меня есть конвенция в этой области, которая служила мне хорошо

для запросов одного элемента:

  • Create... возвращает новый экземпляр, или бросает
  • Get... возвращает ожидаемый существующий экземпляр, или бросает
  • GetOrCreate... возвращает существующий экземпляр или новый экземпляр, если он не существует,или бросает
  • Find... возвращает существующий экземпляр, если он существует, или null

для запросов коллекция:

  • Get... всегда возвращает коллекцию, которая пуста, если нет соответствия[1] элементов

[1] заданы некоторые критерии, явные или неявные, заданные в имени функции или в качестве параметров.


для определенных сценариев вы хотите заметить сбой, как только это произойдет.

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

кроме того, игнорирование ошибок может привести к эксплойтам безопасности. Возможно, null-ness произошел от того, что буфер был перезаписан или тому подобное. Теперь ты ... не сбой, что означает, что у эксплуататора есть шанс выполнить в вашем коде.


какие альтернативы вы видите для возврата null?

Я вижу два случая:

  • findAnItem (id). Что делать, если элемент не найден

в этом случае мы могли бы: вернуть Null или бросить (проверено) исключение (или, возможно, создать элемент и вернуть его)

  • listItemsMatching (критерии) что это должно вернуть, если ничего не найдено?

в этом случае мы можем вернуть Null, вернуть пустой список или выдать исключение.

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

x = find();
x.getField();  // bang null pointer exception

в Java, бросая проверенное исключение, RecordNotFoundException, позволяет компилятору напомнить клиенту иметь дело с case.

Я считаю, что поиск, возвращающий пустые списки, может быть довольно удобным - просто заполните дисплей всем содержимым списка, о он пуст, код "просто работает".


основная идея этого потока-программировать оборонительно. То есть код против неожиданного. Существует множество различных ответов:

Адамский предлагает посмотреть на шаблон нулевого объекта, и этот ответ будет проголосован за это предложение.

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

Если мы сделаем "правило", что мы всегда хотим делать оборонительное Программирование, то мы можем видеть, что эти предложения действительны.

но у нас есть 2 сценария развития.

классы "авторские" разработчиком: автор

классы "потребляются" другим (возможно) разработчиком: разработчик

независимо от того, возвращает ли класс NULL для методов с возвращаемым значением или нет, разработчику нужно будет проверить, объект является допустимым.

Если разработчик не может этого сделать, то этот класс/метод не является детерминированным. То есть, если "вызов метода" для получения объекта не делает то, что он "рекламирует" (например, getEmployee), он нарушил контракт.

Как автор класса, я всегда хочу быть добрым и защитным (и детерминированным) при создании метода.

таким образом, учитывая, что либо NULL или нулевой объект (например, if (employee as NullEmployee.ISVALID)) необходимо проверить и это может произойти с коллекцией сотрудников, тогда подход null object является лучшим подходом.

но мне тоже нравится Майкл Валентий-х предложение именования метода, который должен возвращать null, например getEmployeeOrNull.

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

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

таким образом, как разработчик, я бы запрограммировал защиту от NULL из метода. Если автор дал мне контракт, который всегда возвращает объект (NULL OBJECT всегда делает) и этот объект имеет метод / свойство, с помощью которого можно проверить действительность объекта, тогда я бы использовал это метод / свойство для продолжения использования объекта, иначе объект недопустим и я не могу им воспользоваться.

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

разработчик всегда должен использовать защитное программирование для проверки правильности возвращаемых объектов из другого класса/метода.

в отношении

GregJF


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


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


другие варианты для этого: возврат некоторого значения, указывающего на успех или нет (или тип ошибки), но если вам просто нужно логическое значение, которое будет указывать на успех / сбой, возвращая null для сбоя, а объект для успеха не будет менее правильным, то возврат true/false и получение объекта через параметр.
Другим подходом было бы использовать исключение для указания сбоев, но здесь-есть на самом деле еще много голосов, которые говорят, что это плохая практика (как использование исключения могут быть удобными, но имеют много недостатков).
Поэтому я лично не вижу ничего плохого в возврате null как указание на то, что что-то пошло не так, и проверка его позже (чтобы на самом деле знать, удалось ли вам или нет). Кроме того, слепо думая, что ваш метод не будет возвращать NULL, а затем основывать на нем свой код, может привести к другим, иногда труднодоступным ошибкам (хотя в большинстве случаев это просто приведет к сбою вашей системы :), так как вы будете ссылаться на 0x00000000 рано или поздно позже.)


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

нулевая функция или метод часто используются в качестве поведения по умолчанию revectorable функции или переопределяемого метода в объектной структуре.

Null_function @wikipedia


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


Если код что-то вроде:

command = get_something_to_do()

if command:  # if not Null
    command.execute()

Если у вас есть фиктивный объект, метод execute () которого ничего не делает, и вы возвращаете это вместо Null в соответствующих случаях, вам не нужно проверять нулевой случай и вместо этого можете просто сделать:

get_something_to_do().execute()

Итак, здесь проблема не между проверкой NULL и исключением, а между вызывающим абонентом, который должен обрабатывать специальные Не-случаи по-разному (каким-либо образом) или нет.


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


Добрый день,

возврат NULL, когда вы не можете создать новый объект, является стандартной практикой для многих API.

Почему, черт возьми, это плохой дизайн, я понятия не имею.

Edit: это верно для языков, где у вас нет исключений, таких как C, где это было соглашение в течение многих лет.

HTH

'Avahappy,