Я слышал некоторые голоса, говорящие, что проверка возвращаемого значения null из методов - плохой дизайн. Я хотел бы услышать некоторые причины для этого.
псевдокод:
variable x = object.method()
if (x is null) do something
Я слышал некоторые голоса, говорящие, что проверка возвращаемого значения null из методов - плохой дизайн. Я хотел бы услышать некоторые причины для этого.
псевдокод:
variable x = object.method()
if (x is null) do something
Обоснование отсутствия возвращаемого значения null заключается в том, что вам не нужно проверять его, и, следовательно, вашему коду не требуется следовать другому пути на основе возвращаемого значения. Возможно, вы захотите проверить Null Object Pattern, в котором содержится дополнительная информация об этом.
Например, если бы я должен был определить метод в 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 - ужасный дизайн в объектно-ориентированном мире. Вкратце, использование NULL приводит к:
Проверьте это сообщение в блоге для подробного объяснения: http://www.yegor256.com/2014/05/13/why-null-is-bad.html. Больше в моей книге " Элегантные объекты", раздел 4.1.
Кто сказал, что это плохой дизайн?
Проверка нулей является обычной практикой, даже поощряемой, иначе вы рискуете NullReferenceExceptions повсюду. Лучше обрабатывать ошибку грациозно, чем бросать исключения, когда вам это не нужно.
Основываясь на том, что вы сказали до сих пор, я думаю, что информации недостаточно.
Возврат null из метода CreateWidget() кажется плохим.
Возврат null из метода FindFooInBar() кажется прекрасным.
Его изобретатель говорит, что это ошибка в миллиард долларов!
Это зависит от языка, который вы используете. Если у вас такой язык, как С#, где идиоматический способ указывать на отсутствие значения - это вернуть значение null, то возврат нулевого значения является хорошим дизайном, если у вас нет значения. В качестве альтернативы, в таких языках, как Haskell, которые идиоматически используют монадию Maybe для этого случая, то возврат null будет плохим дизайном (если бы это было возможно).
Если вы прочтете все ответы, становится ясно, что ответ на этот вопрос зависит от метода.
Во-первых, когда происходит нечто исключительное (IOproblem и т.д.), логически исключаются исключения. Когда что-то исключительное, возможно, что-то для другой темы.
Всякий раз, когда ожидается, что метод не будет иметь никаких результатов, существуют две категории:
До тех пор, пока у нас не будет официального способа обозначить, что функция может или не может вернуть значение null, я пытаюсь обозначить это соглашение об именах. Так же, как у вас есть соглашение TrySomething() для методов, которые, как ожидается, будут терпеть неудачу, я часто называю мои методы SafeSomething(), когда метод возвращает нейтральный результат вместо нуля.
Я еще не совсем в порядке с именем, но не мог придумать ничего лучшего. Так что я бегу с этим пока.
Прекрасно возвращать значение null, если это имеет смысл в некотором роде:
public String getEmployeeName(int id){ ..}
В таком случае имеет смысл возвращать значение null, если идентификатор не соответствует существующему объекту, поскольку он позволяет различать случай, когда совпадение не найдено из допустимой ошибки.
Люди могут подумать, что это плохо, потому что его можно злоупотреблять как "специальное" возвращаемое значение, которое указывает на состояние ошибки, что не так хорошо, немного напоминает возврат кодов ошибок из функции, но сбивает с толку, потому что пользователь должен проверить return for null, вместо улавливания соответствующих исключений, например
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
Это не обязательно плохой дизайн - как и во многих дизайнерских решениях, это зависит.
Если результатом метода является то, что не будет иметь хорошего результата при нормальном использовании, возврат нулевого значения прекрасен:
object x = GetObjectFromCache(); // return null if it 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 likely that it OK to not have the try/catch since you won't
// be able to really handle the problem here
}
catch /* ... */ {
}
Исключения для исключений всех обстоятельств.
Если ваша функция предназначена для поиска атрибута, связанного с данным объектом, и этот объект не имеет такого атрибута, может оказаться уместным вернуть значение null. Если объект не существует, бросание исключения может быть более уместным. Если функция предназначена для возврата списка атрибутов, и их нет, возврат пустого списка имеет смысл - вы возвращаете все нулевые атрибуты.
У меня есть соглашение в этой области, которое хорошо меня обслуживало
Для запросов отдельных элементов:
Create...
возвращает новый экземпляр или бросаетGet...
возвращает ожидаемый существующий экземпляр или бросаетGetOrCreate...
возвращает существующий экземпляр или новый экземпляр, если он не существует, или выбрасываетFind...
возвращает существующий экземпляр, если он существует, или null
Для запросов на сбор:
Get...
всегда возвращает коллекцию, которая пуста, если не найдено соответствующих [1] элементов[1] заданы некоторые критерии, явные или неявные, заданные в имени функции или как параметры.
В некоторых сценариях вы хотите заметить сбой, как только это произойдет.
Проверка на NULL и отсутствие утверждения (для ошибок программиста) или бросание (для ошибок пользователя или вызывающего абонента) в случае сбоя могут означать, что последующие сбои сложнее отслеживать, поскольку исходный нечетный случай не найден.
Кроме того, игнорирование ошибок может привести к эксплойтам безопасности. Возможно, нуль возник из-за того, что буфер был перезаписан или тому подобное. Теперь вы не сбой, а это значит, что у эксплуататора есть шанс выполнить в вашем коде.
Какие альтернативы вы видите для возврата null?
Я вижу два случая:
В этом случае мы могли бы: вернуть Null или выбросить исключение (отмеченное) (или, возможно, создать элемент и вернуть его)
В этом случае мы можем вернуть Null, вернуть пустой список или выбросить исключение.
Я считаю, что возврат null может быть менее хорошим, чем альтернативы, потому что он требует, чтобы клиент помнил, чтобы проверить значение null, программисты забывают и кодируют
x = find();
x.getField(); // bang null pointer exception
В Java, бросая проверенное исключение, RecordNotFoundException, позволяет компилятору напомнить клиенту о том, чтобы иметь дело с случаем.
Я нахожу, что поиски, возвращающие пустые списки, могут быть весьма удобными - просто заполняйте дисплей всем содержимым списка, а он пуст, код "просто работает".
Иногда возвращение NULL - это правильная вещь, но, в частности, когда вы имеете дело с последовательностями разных сортов (массивы, списки, строки, то, что есть), вероятно, лучше возвращать последовательность нулевой длины, так как это приводит к более коротким и, надеюсь, более понятным кодам, хотя и не требует больше написания на стороне API-интерфейса.
Заставьте их вызвать другой метод после факта, чтобы выяснить, был ли предыдущий вызов нулевым.;-) Эй, это было достаточно хорошо для JDBC
Основная идея этого потока - запрограммировать защиту. То есть, код против неожиданного. Существует множество ответов:
Adamski предлагает посмотреть на шаблон Null Object Pattern, причем этот ответ будет проголосован за это предложение.
Michael Valenty также предлагает соглашение об именах, чтобы сообщить разработчику, что можно ожидать. ZeroConcept предлагает правильное использование Exception, если это причина NULL. И другие.
Если мы сделаем "правило", что всегда хотим сделать защитное программирование, мы можем видеть, что эти предложения действительны.
Но у нас есть 2 сценария разработки.
Классы, "разработанные" разработчиком: Автор
Классы, "потребляемые" другим (возможно) разработчиком: Разработчик
Независимо от того, возвращает ли класс NULL для методов с возвращаемым значением или нет, разработчику необходимо будет проверить, действительно ли объект.
Если разработчик не может этого сделать, то этот класс/метод не является детерминированным. То есть, если "вызов метода" для получения объекта не выполняет то, что он "рекламирует" (например, getEmployee), он нарушил контракт.
Как автор класса, я всегда хочу быть добрым и защитным (и детерминированным) при создании метода.
Таким образом, при условии, что необходимо указать NULL или NULL OBJECT (например, если (сотрудник как NullEmployee.ISVALID)) и это может потребоваться с коллекцией сотрудников, тогда подход с нулевым объектом является лучшим подходом.
Но мне также нравится предложение Michael Valenty's именования метода, который ДОЛЖЕН возвращать null, например getEmployeeOrNull.
Автор, который генерирует исключение, удаляет выбор разработчика для проверки действительности объекта, что очень плохо для коллекции объектов и заставляет разработчика работать с исключениями при ветвлении их потребительского кода.
Как разработчик, потребляющий класс, я надеюсь, что автор дает мне возможность избежать или запрограммировать нулевую ситуацию что их класс/методы могут столкнуться с.
Так как разработчик, я бы запрограммировал защиту против NULL из метода. Если автор дал мне контракт, который всегда возвращает объект (всегда имеет значение NULL OBJECT) и этот объект имеет метод/свойство, с помощью которого можно проверить достоверность объекта, то я бы использовал этот метод/свойство для продолжения использования объекта, иначе объект недействителен и я не могу его использовать.
Нижняя строка заключается в том, что автор класса/методов должен обеспечивать механизмы которые разработчик может использовать в своем защитном программировании. То есть, более ясное намерение метода.
Разработчик должен всегда использовать защитное программирование для проверки достоверности возвращаемых объектов из другого класса/метода.
приветствует
GregJF
Другими вариантами этого являются:
возвращая некоторое значение, которое указывает на успех или нет (или тип ошибки), но если вам просто нужно логическое значение, которое будет указывать на успех/неудачу, возвращая значение null для отказа, а объект для успеха будет не менее корректным, а затем вернется true/false и получение объекта через параметр.
Другой подход заключался бы в том, чтобы использовать исключение для обозначения сбоев, но здесь - на самом деле существует гораздо больше голосов, которые говорят, что это практическая практика (поскольку использование исключений может быть удобным, но имеет множество недостатков).
Поэтому я лично не вижу ничего плохого в возвращении null как признаке того, что что-то пошло не так, и проверив его позже (чтобы действительно знать, преуспели вы или нет). Кроме того, слепо думая, что ваш метод не вернет NULL, а затем основать ваш код на нем, может привести к другим, иногда трудно найти, ошибкам (хотя в большинстве случаев это просто приведет к краху вашей системы:), поскольку вы ссылаетесь на 0x00000000 рано или поздно).
Непреднамеренные нулевые функции могут возникать при разработке сложных программ и, как и мертвый код, такие случаи указывают на серьезные недостатки в структурах программ.
Нулевая функция или метод часто используются в качестве поведения по умолчанию для переопределяемой функции или переопределяемого метода в рамках объекта.
Ну, это точно зависит от цели метода... Иногда лучшим выбором было бы исключение. Все зависит от случая к случаю.
Если код выглядит примерно так:
command = get_something_to_do()
if command: # if not Null
command.execute()
Если у вас есть фиктивный объект, метод execute() ничего не делает, и вы возвращаете это вместо Null в соответствующих случаях, вам не нужно проверять нулевой регистр и вместо этого просто делать:
get_something_to_do().execute()
Итак, здесь проблема заключается не в проверке на NULL и на исключение, а вместо этого между вызывающим устройством приходится обрабатывать специальные не-случаи по-разному (каким бы то ни было способом) или нет.
Для моего случая использования мне нужно было вернуть Map из метода, а затем найти конкретный ключ. Но если я верну пустую карту, это приведет к исключению NullPointerException, и тогда это не будет сильно отличаться, возвращая null вместо пустой карты. Но из Java8 дальше мы могли бы использовать Факультативно. Вышесказанное является той самой причиной, по которой была введена дополнительная концепция.
G'day,
Возвращение NULL, когда вы не можете создать новый объект, является стандартной практикой для многих API.
Почему, черт возьми, это плохой дизайн, я понятия не имею.
Изменить: Это относится к языкам, на которых у вас нет исключений, таких как C, где это соглашение уже много лет.
НТН
'Avahappy,