Например, предположим, у меня есть класс Member
, который имеет lastChangePasswordTime:
class Member{
.
.
.
constructor(){
this.lastChangePasswordTime=null,
}
}
у которого lastChangePasswordTime может отсутствовать, потому что некоторые участники никогда не могут менять свои пароли.
Но в соответствии с тем, что пустые значения являются злом, что следует использовать, когда значение может отсутствовать по значению? и https://softwareengineering.stackexchange.com/a/12836/248528 , я не должен использовать null для представления значимо отсутствующего значения. Поэтому я пытаюсь добавить логический флаг:
class Member{
.
.
.
constructor(){
this.isPasswordChanged=false,
this.lastChangePasswordTime=null,
}
}
Но я думаю, что это довольно устарело, потому что:
Когда isPasswordChanged имеет значение false, lastChangePasswordTime должно иметь значение null, а проверка lastChangePasswordTime == null практически идентична проверке isPasswordChanged - false, поэтому я предпочитаю проверить lastChangePasswordTime == null напрямую
При изменении логики здесь я могу забыть обновить оба поля.
Примечание: когда пользователь меняет пароли, я записываю время следующим образом:
this.lastChangePasswordTime=Date.now();
Является ли дополнительное булево поле лучше нулевой ссылки здесь?
std::optional
илиOption
. В других языках вам, возможно, придется создать соответствующий механизм самостоятельно, или вы можете прибегнуть кnull
чему-то подобному, потому что это более идиоматично.lastChangePasswordTime
там может быть неинициализированный указатель, и сравнение его с чем угодно будет неопределенным поведением. Не очень убедительная причина не инициализировать указатель наNULL
/nullptr
вместо, особенно не в современном C ++ (где вы вообще не используете указатель), но кто знает? Другим примером могут быть языки без указателей или с плохой поддержкой указателей, возможно. (ФОРТРАН 77 приходит на ум ...)Ответы:
Я не понимаю, почему, если у вас есть значимое отсутствие значения,
null
его не следует использовать, если вы намеренно и осторожно относитесь к нему.Если ваша цель заключается в том, чтобы заключить значение Nullable во избежание случайного обращения к нему, я бы предложил создать
isPasswordChanged
значение в виде функции или свойства, которое возвращает результат нулевой проверки, например:На мой взгляд, делать это так:
isPasswordChanged
вами ценности.То, как вы сохраняете данные (предположительно в базе данных), будет отвечать за сохранение нулей.
источник
isPasswordChanged
же, как вы можете забыть, чтобы проверить, если это ноль. Я не вижу здесь ничего полезного.nulls
не злые Использование их без размышлений есть. Это тот случай, когдаnull
точно правильный ответ - даты нет.Обратите внимание, что ваше решение создает больше проблем. Что означает дата, установленная для чего-то, но
isPasswordChanged
ложная? Вы только что создали случай противоречивой информации, которую нужно специально отловить и обработать, в то время какnull
значение имеет четко определенное, однозначное значение и не может противоречить другой информации.Так что нет, ваше решение не лучше. Принятие
null
значения является правильным подходом здесь. Люди, которые утверждают, чтоnull
это всегда зло, независимо от контекста, не понимают, почемуnull
существует.источник
null
существует, потому что Тони Хоар допустил ошибку в миллиард долларов в 1965 году. Люди, которые утверждают, чтоnull
это «зло», конечно, слишком упрощают тему, но хорошо, что современные языки или стили программирования отходятnull
, заменяя это с выделенными типами опций.DateTime
поле является указателем. Вся концепцияnull
имеет смысл только для указателей!В зависимости от вашего языка программирования, могут быть хорошие альтернативы, такие как
optional
тип данных. В C ++ (17) это будет std :: необязательный . Он может отсутствовать или иметь произвольное значение базового типа данных.источник
Optional
в яве; смысл нольOptional
быть на вашеOptional<Date> lastPasswordChangeTime = null;
, но я бы не сдался только из-за этого. Вместо этого я хотел бы привить очень твердое командное правило, согласно которому никакие необязательные значения никогда не могут быть назначеныnull
. Факультативно покупает слишком много приятных функций, чтобы легко отказаться от них. Вы можете легко назначить значения по умолчанию (orElse
или с ленивым вычислением:)orElseGet
, выбросить ошибки (orElseThrow
), преобразовать значения в нулевой безопасный способ (map
,flatmap
) и т. Д.isPresent
- это почти всегда запах кода и довольно надежный признак того, что разработчики, использующие эти опции, «упускают суть». На самом деле, это не дает никакой пользыref == null
, но вы также не должны часто пользоваться этим. Даже если команда нестабильна, инструмент статического анализа может установить закон, такой как linter или FindBugs , который может поймать большинство случаев.null
потому что язык является 100% совместим с Java, но никто не использует его, иOption[T]
это повсюду, с отличной функциональной программирования поддержки , какmap
,flatMap
, сопоставления с образцом и так далее, который идет далеко, далеко за пределы== null
проверок. Вы правы в том, что в теории тип параметра звучит немного больше, чем прославленный указатель, но реальность доказывает, что этот аргумент неверен.Правильно используя нуль
Есть разные способы использования
null
. Наиболее распространенный и семантически правильный способ - использовать его, когда вы можете иметь или не иметь единственное значение. В этом случае значение либо равно,null
либо является чем-то значимым, например запись из базы данных или что-то еще.В этих ситуациях вы в основном используете это так (в псевдокоде):
проблема
И это очень большая проблема. Проблема в том, что к тому времени, когда вы вызываете
doSomethingUseful
значение, возможно, не было провереноnull
! Если этого не произошло, программа, скорее всего, вылетит. И пользователь может даже не увидеть никаких хороших сообщений об ошибках, оставаясь с чем-то вроде «ужасная ошибка: хотел значение, но получил ноль!» (после обновления: хотя может быть даже менее информативная ошибка, напримерSegmentation fault. Core dumped.
, или, что еще хуже, в некоторых случаях нет ошибок и неправильных манипуляций с нулем)Забыть писать чеки
null
и обрабатыватьnull
ситуации - очень распространенная ошибка . Вот почему изобретатель Тони Хоарnull
сказал на конференции по программному обеспечению QCon London в 2009 году, что он допустил ошибку в миллиард долларов в 1965 году: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Ошибка-Тони ХоараКак избежать проблемы
Некоторые технологии и языки делают проверку на
null
невозможность забыть по-разному, уменьшая количество ошибок.Например, Haskell имеет
Maybe
монаду вместо нулей. Предположим, чтоDatabaseRecord
это определенный пользователем тип. В Haskell значение типаMaybe DatabaseRecord
может быть равнымJust <somevalue>
или равнымNothing
. Затем вы можете использовать его по-разному, но независимо от того, как вы его используете, вы не сможете применить некоторые операции,Nothing
не зная об этом.Например, эта вызываемая функция
zeroAsDefault
возвращаетx
дляJust x
и0
дляNothing
:Кристиан Хакл говорит, что C ++ 17 и Scala имеют свои собственные пути. Поэтому вы можете попытаться выяснить, есть ли на вашем языке что-то подобное, и использовать его.
Нули все еще широко используются
Если у вас нет ничего лучше, тогда
null
пользуйтесь. Просто продолжайте следить за этим. Объявления типов в функциях помогут вам в любом случае.Также это может звучать не очень прогрессивно, но вы должны проверить, хотят ли ваши коллеги использовать
null
или что-то еще. Они могут быть консервативными и могут не хотеть использовать новые структуры данных по некоторым причинам. Например, поддержка старых версий языка. Такие вещи должны быть заявлены в стандартах кодирования проекта и должным образом обсуждены с командой.По вашему предложению
Вы предлагаете использовать отдельное логическое поле. Но вы все равно должны это проверить и все же можете забыть это проверить. Так что здесь ничего не выиграно. Если вы даже можете забыть что-то еще, например, каждый раз обновлять оба значения, то это еще хуже. Если проблема забвения чека
null
не решена, тогда нет смысла. Избегатьnull
трудно, и вы не должны делать это таким образом, чтобы это еще хуже.Как не использовать нуль
Наконец, есть общие способы использования
null
неправильно. Один из таких способов - использовать его вместо пустых структур данных, таких как массивы и строки. Пустой массив - это правильный массив, как и любой другой! Почти всегда важно и полезно, чтобы структуры данных, которые могут соответствовать нескольким значениям, могли быть пустыми, то есть иметь длину 0.С точки зрения алгебры пустая строка для строк очень похожа на 0 для чисел, то есть тождество:
Пустая строка позволяет строкам в целом стать моноидом: https://en.wikipedia.org/wiki/Monoid Если вы его не получите, это не так важно для вас.
Теперь давайте посмотрим, почему это важно для программирования на следующем примере:
Если мы передадим пустой массив здесь, код будет работать нормально. Это просто ничего не сделает. Однако, если мы передадим
null
здесь, то, скорее всего, получим сбой с ошибкой типа «не могу пройти через ноль, извините». Мы могли бы обернуть это,if
но это не так чисто, и опять же, вы можете забыть проверить этоКак обращаться с нулем
Что
doSomethingAboutIt()
следует делать, и особенно то, должно ли оно генерировать исключение, это еще один сложный вопрос. Короче говоря, это зависит от того,null
было ли приемлемое входное значение для данной задачи и что ожидается в ответ. Исключения составляют события, которые не были ожидаемы. Я не буду углубляться в эту тему. Этот ответ очень длинен уже.источник
horrible error: wanted value but got null!
это намного лучше, чем более типичныйSegmentation fault. Core dumped.
...Error: the product you want to buy is out of stock
.null
туда, гдеnull
не разрешено, является ошибкой программирования, то есть ошибкой в коде. Отсутствие товара - это часть обычной бизнес-логики, а не ошибка. Вы не можете и не должны пытаться перевести обнаружение ошибки в обычное сообщение в пользовательском интерфейсе. Немедленное завершение работы и отказ от выполнения большего количества кода является предпочтительным вариантом действий, особенно если это важное приложение (например, приложение, которое выполняет реальные финансовые транзакции).null
полностью удалить себя, даже из фона.В дополнение ко всем очень хорошим ответам, которые были даны ранее, я бы добавил, что каждый раз, когда у вас возникает желание оставить поле пустым, тщательно подумайте, может ли это быть тип списка. Обнуляемый тип эквивалентен списку из 0 или 1 элементов, и часто его можно обобщить до списка из N элементов. В этом случае, возможно, вы захотите рассмотреть
lastChangePasswordTime
списокpasswordChangeTimes
.источник
Задайте себе вопрос: какое поведение требует поле lastChangePasswordTime?
Если вам нужно это поле для метода IsPasswordExpired (), чтобы определить, нужно ли участнику запрашивать смену пароля так часто, я бы установил в поле время, когда он был изначально создан. Реализация IsPasswordExpired () одинакова для новых и существующих членов.
Если у вас есть отдельное требование о том, что вновь создаваемые участники должны обновить свой пароль, я бы добавил отдельное логическое поле с именем passwordShouldBeChanged и установил бы его в значение true при создании. Затем я бы изменил функциональность метода IsPasswordExpired (), чтобы включить проверку для этого поля (и переименовал метод в ShouldChangePassword).
Сделайте ваши намерения явными в коде.
источник
Во-первых, нулевые значения, являющиеся злом, являются догмой, и, как обычно, с догмой, они работают лучше всего в качестве ориентира, а не в качестве теста «сдать / не пройти».
Во-вторых, вы можете переопределить вашу ситуацию таким образом, чтобы иметь смысл, что значение никогда не может быть нулевым. InititialPasswordChanged - это логическое значение, изначально установленное на false, PasswordSetTime - это дата и время, когда был установлен текущий пароль.
Обратите внимание, что хотя это и требует небольших затрат, теперь вы ВСЕГДА можете рассчитать, сколько времени прошло с момента последней установки пароля.
источник
Оба являются «безопасными / нормальными / правильными», если вызывающий проверяет перед использованием. Проблема в том, что произойдет, если звонящий не проверит. Что лучше, какая-то разновидность нулевой ошибки или использование недопустимого значения?
Нет единого правильного ответа. Это зависит от того, что вы беспокоитесь.
Если сбои действительно плохие, но ответ не является критическим или имеет принятое значение по умолчанию, тогда, возможно, лучше использовать логическое значение в качестве флага. Если использование неправильного ответа является более серьезной проблемой, чем сбой, тогда лучше использовать null.
Для большинства «типичных» случаев быстрый сбой и принуждение вызывающих абонентов к проверке - это самый быстрый путь к здравому коду, и, следовательно, я думаю, что значение по умолчанию должно быть null Я бы не стал слишком верить в евангелистов «Икс - корень всех злых»; они обычно не ожидали всех вариантов использования.
источник