Если пустые значения являются злом, что следует использовать, если значение может отсутствовать по значению?


26

Это одно из правил, которые повторяются снова и снова, и это сбивает меня с толку.

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

Но, но - из-за моей наивности, позвольте мне закричать - иногда значение МОЖЕТ быть значимо отсутствовать!

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

После каждого хода сервер транслирует список обновлений JS-кода на стороне клиента. Класс обновления:

public class GameUpdate
{
    public Player player;
    // other stuff
}

Этот класс сериализуется в JSON и отправляется подключенным игрокам.

У большинства обновлений, естественно, есть связанный с ними игрок - в конце концов, необходимо знать, какой игрок сделал ход на этом ходу. Однако некоторые обновления не могут иметь игрока, значимо связанного с ними. Пример: игра была принудительно связана, потому что предел хода без действий был превышен. Я думаю, что для таких обновлений имеет смысл сделать игрока обнуленным.

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

public class GameUpdate
{
    // stuff
}

public class GamePlayerUpdate : GameUpdate
{
    public Player player;
    // other stuff
}

Однако я не вижу, как это улучшится, по двум причинам:

  • Код JS теперь просто получит объект без Player в качестве определенного свойства, которое будет таким же, как если бы оно было нулевым, поскольку оба случая требуют проверки, присутствует ли значение или отсутствует;
  • Если к классу GameUpdate будет добавлено еще одно обнуляемое поле, мне придется иметь возможность использовать множественное наследование для продолжения этого дизайна - но MI само по себе зло (по мнению более опытных программистов) и, что более важно, C # не делает так что я не могу его использовать.

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

Не могли бы вы объяснить мне эту проблему?


2
Этот вопрос специфичен для JavaScript? Во многих языках есть тип Optional для этой цели, но я не знаю, есть ли у js один (хотя вы могли бы его создать) или он работал бы с json (хотя это одна часть мерзких проверок нуля, а не всего вашего кода
Ричард Тингл

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

1
@usr - Я полностью согласен. Нуль твой друг. ИМХО, нет более понятного способа представления отсутствующего значения. Он может помочь вам найти ошибки, может помочь вам справиться с ошибочными условиями, он может помочь вам просить прощения (попытаться / поймать). Что не нравится ?!
Скуба Стив

4
Одним из первых правил разработки программного обеспечения является «Всегда проверять на ноль». Почему это даже проблема, ошеломляет меня.
MattE

1
@MattE - Абсолютно. Зачем мне разрабатывать шаблон проектирования, когда на самом деле Null - это хорошая вещь, которую нужно иметь в вашем базовом наборе инструментов. То, что предлагал Грэм, ИМХО, кажется мне чрезмерным.
Скуба Стив

Ответы:


20

Много вещей лучше вернуть, чем ноль.

  • Пустая строка ("")
  • Пустая коллекция
  • «Необязательная» или «возможно» монада
  • Функция, которая тихо ничего не делает
  • Объект, полный методов, которые тихо ничего не делают
  • Значимое значение, которое отвергает неверную предпосылку вопроса
    (именно это заставило вас считать нулевым в первую очередь)

Хитрость заключается в том, чтобы понять, когда это сделать. Null действительно хорош для того, чтобы взорваться на твоем лице, но только тогда, когда ты усеиваешь его, не проверяя его. Нехорошо объяснять почему.

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

Монады звучат причудливо, но здесь все, что они делают, это позволяют вам показать, что действительные размеры для коллекции - 0 и 1. Если они у вас есть, рассмотрите их.

Любой из них лучше прояснит ваши намерения, чем нулевой. Это самое важное отличие.

Это может помочь понять, почему вы вообще возвращаетесь, а не просто выбросить исключение. Исключения - это, по сути, способ отклонить предположение (это не единственный способ). Когда вы спрашиваете о точке пересечения двух линий, вы предполагаете, что они пересекаются. Они могут быть параллельными. Вы должны бросить исключение "параллельных линий"? Ну, вы могли бы, но теперь вы должны обработать это исключение в другом месте или позволить ему остановить систему.

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

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

Проблема с нулем в том, что это может означать слишком много вещей. Так что это в конечном итоге уничтожает информацию.


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

Вас попросили сделать эту ужасно дорогую вещь совместимой с RFC3.14159, в которой говорится, что между сообщениями должен быть маркер. Маркер не должен светиться вообще. Это должно длиться ровно один импульс. То же количество времени, что обычно светит один цветной свет.

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

Каждый, кто знаком с этим проектом, содрогается при прикосновении к машине или к ее контрольному коду. Это не легко изменить. Чем ты занимаешься? Ну, вы можете погрузиться и начать ломать вещи. Может быть, обновить эти вещи отвратительный дизайн. Да, ты мог бы сделать это намного лучше. Или вы можете поговорить с дворником и начать собирать сгоревшие лампочки.

Это полиморфное решение. Системе даже не нужно знать, что что-то изменилось.


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

Сколько яблок ты мне должен, если я дам тебе 1 яблоко? -1. Потому что я был должен вам 2 яблока со вчерашнего дня. Здесь предположение вопроса «ты мне должен» отвергается с отрицательным числом. Несмотря на то, что вопрос был неправильным, значимая информация закодирована в этом ответе. Отвергая это осмысленно, вопрос не заставляется меняться.

Тем не менее, иногда изменение вопроса на самом деле путь. Если предположение можно сделать более надежным, но все же полезным, подумайте об этом.

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

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

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

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

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


9
«вернуть коллекцию, если в ней только 0 или 1 элемент» - извините, но я не думаю, что получаю ее :( Из моего POV выполнение этого (a) добавляет ненужные недопустимые состояния в класс (b) требует документирования того, что в коллекции должно быть не более 1 элемента (с), по-видимому, не решает проблему в любом случае, поскольку проверка, если коллекция содержит единственный элемент, перед тем как обращаться к нему, в любом случае необходима, иначе будет
выдано

2
@gaazkam видишь? Я говорил тебе, что это мешает людям. Но это именно то, что вы делали каждый раз, когда используете пустую строку.
candied_orange

16
Что происходит, когда существует допустимое значение, которое оказывается пустой строкой или множеством?
Blrfl

5
В любом случае @gaazkam, вы явно не проверяете, есть ли в коллекции предметы. Зацикливание (или отображение) над пустым списком является безопасным действием, которое не имеет никакого эффекта.
RubberDuck

3
Я знаю, что вы отвечаете на вопрос о том, что должен делать кто-то, вместо того, чтобы возвращать ноль, но я действительно не согласен со многими из этих пунктов. Однако , как мы не от языка , и есть причина , чтобы разорвать все правила , я отказаться от downvote
Liath

15

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

Давайте посмотрим, как решить эту проблему лучше.

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

Существуют языки программирования, которые обеспечивают безопасность от нулевых указателей (так называемая безопасность void) во время компиляции. Приведите несколько современных примеров: Kotlin, Rust, Swift. В этих языках проблема недостающей информации воспринималась всерьез, и использование null там совершенно безболезненно - компилятор остановит вас от разыменования нулевых указателей.
В Java были предприняты усилия для создания аннотаций @ Nullable / @ NotNull вместе с плагинами компилятора + IDE для достижения того же эффекта. Это не было действительно успешно, но это выходит за рамки для этого ответа.

Явный универсальный тип, обозначающий возможное отсутствие, является еще одним способом решения этой проблемы. Например на Яве есть java.util.Optional. Это может работать:
на уровне проекта или компании вы можете установить правила / руководящие принципы

  • использовать только качественные сторонние библиотеки
  • всегда используйте java.util.Optionalвместоnull

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


Не могли бы вы пояснить, чем Java Optionals лучше, чем null? Когда я читаю документацию , попытка получить значение из необязательного создает исключение, если значение отсутствует; так что может показаться, что мы в одном месте: проверка необходима, и если мы ее забудем, мы облажались?
Гаазкам

3
@gaazkam Вы правы, он не обеспечивает безопасность во время компиляции. Кроме того, сама опция может быть нулевой, другая проблема. Но это явно (по сравнению с переменными, которые могут быть просто нулевыми комментариями). Это дает только реальное преимущество, если для всей кодовой базы установлено правило кодирования. Вы не можете установить вещи в ноль. Если информация может отсутствовать, используйте Необязательно. Это заставляет откровенность. Обычные нулевые проверки становятся звонкамиOptional.isPresent
marstato

8
@marstato замена нулевых проверок isPresent() не является рекомендуемым использованием Optional
candied_orange

10

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

Нулевое значение может быть использовано неправильно, как «магическое значение», когда оно действительно что-то значит. Но вам нужно было бы сделать довольно запутанное программирование, чтобы попасть туда.

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

При использовании null вы попадаете во время выполнения, где вы попадаете во время компиляции с какой-то типобезопасной альтернативой NULL.

Не совсем верно, я могу получить предупреждение о том, что не проверять нулевое значение перед использованием переменной. И это не то же самое. Возможно, я не хочу щадить ресурсы объекта, который не имеет цели. И что еще более важно, мне придется проверить альтернативу так же, чтобы получить желаемое поведение. Если я забуду это сделать, я скорее получу исключение NullReferenceException во время выполнения, чем какое-то неопределенное / непреднамеренное поведение.

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

Проблема с нулем в том, что это может означать многое. Так что это в конечном итоге уничтожает информацию.

Нет, это ничего не значит, поэтому нечего разрушать. Имя и тип переменной / аргумента всегда скажут, чего не хватает, это все, что нужно знать.

Редактировать:

Я на полпути к видео candied_orange, связанному в одном из его других ответов о функциональном программировании, и оно очень интересно. Я вижу полезность этого в определенных сценариях. Функциональный подход, который я видел до сих пор, с кусками кода, летящими вокруг, обычно заставляет меня съеживаться при использовании в настройках OO. Но я думаю, это имеет свое место. Где-то. И я предполагаю, что будут языки, которые хорошо справляются со своей задачей, скрывая то, что я воспринимаю как запутанный беспорядок, когда сталкиваюсь с этим в C #, языки, которые вместо этого предоставляют чистую и работоспособную модель.

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

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


9
Нуль уничтожает знание о том, что ничего не представляет. Там нет ничего такого, что можно спокойно игнорировать. Вид ничего, что должно остановить систему, чтобы избежать недопустимого состояния. Ничего такого, что означает, что программист облажался и должен исправить код. Ничего такого, что означает, что пользователь попросил что-то, что не существует и должно быть вежливо сказано. Простите, нет. Все они были закодированы как ноль. Из-за этого, если вы закодируете любой из них как ноль, вы не должны удивляться, если никто не знает, что вы имеете в виду. Вы заставляете нас угадывать из контекста.
candied_orange

3
@candied_orange Вы можете использовать точно такой же аргумент для любого необязательного типа: NoneПредставляет ли ….
Дедупликатор

3
@candied_orange Я до сих пор не понимаю, что вы имеете в виду. Разные виды ничего? Если вы используете null, когда вы должны были использовать enum, возможно? Существует только один вид ничего. Причина, по которой что-то должно быть ничем, может варьироваться, но это не меняет ни ничто, ни соответствующий ответ на то, что что-то ничто. Если вы ожидаете значение в магазине, которого нет и которое заслуживает внимания, вы бросаете или регистрируете в момент запроса и ничего не получаете, вы не передаете null в качестве аргумента методу, а затем в этом методе пытаетесь выяснить почему вы получили ноль. Нуль не будет ошибкой.
Мартин Маат

2
Ноль - это ошибка, потому что у вас была возможность закодировать значение «ничего». Ваше понимание ничего не требует немедленной обработки ничего. 0, null, NaN, пустая строка, пустое множество, void, нулевой объект, не применимо, не реализовано исключение, во многих многих формах ничего не встречается. Вам не нужно забывать то, что вы знаете сейчас, просто потому, что вы не хотите иметь дело с тем, что вы знаете сейчас. Если я попрошу вас взять квадратный корень из -1, вы можете либо сгенерировать исключение, чтобы сообщить мне, что это невозможно, и забыть все, либо придумать воображаемые числа и сохранить то, что вы знаете.
candied_orange

1
@MartinMaat, вы говорите так, будто функция должна выдавать исключение каждый раз, когда ваши ожидания нарушаются. Это просто неправда. Часто есть угловые случаи, чтобы иметь дело с. Если вы действительно не видите этого, прочитайте это
candied_orange

7

Ваш вопрос.

Но, но - из-за моей наивности, позвольте мне закричать - иногда значение МОЖЕТ быть значимо отсутствовать!

Полностью на борту с вами там. Тем не менее, есть несколько предостережений, о которых я расскажу через секунду. Но сначала:

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

Я думаю, что это благими намерениями, но чрезмерно обобщенное заявление.

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

Но я не понимаю, как отказ от проверки на ноль является каким-то доказательством того, что нуль по своей сути неправильно использовать.

Если null является злом, то то же самое относится и к индексам массивов и указателям на C ++. Они не. С ними легко выстрелить себе в ногу, но их не следует избегать любой ценой.

В остальной части этого ответа я собираюсь адаптировать намерение «нули - это зло» к более нюансам « нули должны обрабатываться ответственно ».


Ваше решение

Хотя я согласен с вашим мнением об использовании null как глобального правила; Я не согласен с тем, что вы используете null в данном конкретном случае.

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

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

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

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

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

  • Код JS теперь просто получит объект без Player в качестве определенного свойства, которое будет таким же, как если бы оно было нулевым, поскольку оба случая требуют проверки, присутствует ли значение или отсутствует;

Ваш аргумент опирается на ошибки полиморфизма. Если у вас есть метод, который возвращает GameUpdateобъект, он вообще не должен делать различий между GameUpdate, PlayerGameUpdateили NewlyDevelopedGameUpdate.

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

Это делает ваш аргумент спорным. В хорошей практике вы никогда не должны заботиться о том, что на вашем GameUpdateобъекте есть или нет игрока. Если вы пытаетесь получить доступ к Playerсвойству GameUpdate, то вы делаете что-то нелогичное.

Типизированные языки, такие как C #, даже не позволят вам получить доступ к Playerсвойству a, GameUpdateпотому что GameUpdateпросто не имеют этого свойства. Javascript значительно слабее в своем подходе (и не требует предварительной компиляции), поэтому он не пытается исправить вас и взорвет его во время выполнения.

  • Если к классу GameUpdate будет добавлено еще одно обнуляемое поле, мне придется иметь возможность использовать множественное наследование для продолжения этого дизайна - но MI само по себе зло (по мнению более опытных программистов) и, что более важно, C # не делает так что я не могу его использовать.

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


Как избежать проблем с нулем вообще?

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

1. В любом случае используйте нуль.

Это самый простой подход. Тем не менее, вы подписываетесь на тонну нулевых проверок и (в случае неудачи) устраняете исключительные ситуации нулевых ссылок.

2. Используйте ненулевое бессмысленное значение

Простой пример здесь indexOf():

"abcde".indexOf("b"); // 1
"abcde".indexOf("f"); // -1

Вместо того null, чтобы получить -1. На техническом уровне это допустимое целочисленное значение. Однако отрицательное значение является бессмысленным ответом, так как ожидается, что индексы будут положительными целыми числами.

По логике это можно использовать только в тех случаях, когда возвращаемый тип допускает больше возможных значений, чем может быть возвращено осмысленно (indexOf = положительные целые числа; int = отрицательные и положительные целые числа)

Для ссылочных типов вы все равно можете применить этот принцип. Например, если у вас есть объект с целочисленным идентификатором, вы можете вернуть фиктивный объект с идентификатором, установленным на -1, в отличие от нуля.
Вам просто нужно определить «фиктивное состояние», с помощью которого вы можете измерить, является ли объект действительно пригодным для использования или нет.

Обратите внимание, что вы не должны полагаться на предопределенные строковые значения, если вы не контролируете строковое значение. Возможно, вы захотите установить имя пользователя на «ноль», если хотите вернуть пустой объект, но у вас возникнут проблемы, когда Кристофер Налл подпишется на ваш сервис .

3. Бросьте исключение вместо.

Нет, просто нет. Исключения не должны обрабатываться для логического потока.

При этом в некоторых случаях вы не хотите скрывать исключение (nullreference), а вместо этого показывать соответствующее исключение. Например:

var existingPerson = personRepository.GetById(123);

Это может выдать PersonNotFoundException(или аналогичный), когда нет человека с идентификатором 123.

Совершенно очевидно, что это приемлемо только в тех случаях, когда отсутствие предмета делает невозможным дальнейшую обработку. Если не удается найти элемент, и приложение может продолжаться, то НИКОГДА не следует выбрасывать исключение . Я не могу подчеркнуть это достаточно.


5

Причина, по которой нулевые значения являются плохими, заключается не в том, что отсутствующее значение кодируется как нулевое, а в том, что любое ссылочное значение может быть нулевым Если у вас есть C #, вы, вероятно, все равно потерялись. Я не вижу, чтобы точка ro использовала там какой-либо необязательный параметр, поскольку ссылочный тип уже является дополнительным. Но для Java есть аннотации @Notnull, которые, кажется, вы можете настроить на уровне пакета , тогда для значений, которые могут быть пропущены, вы можете использовать аннотацию @Nullable.


Да! Проблема бессмысленная по умолчанию.
Дедупликатор

Я не согласен. Программирование в стиле, который избегает нуля, приводит к тому, что меньшее число ссылочных переменных становится нулевым, что приводит к меньшему количеству ссылочных переменных, которые не должны быть равны нулю, будучи нулем. Это не 100%, но, возможно, 90%, что стоит ИМО.
Восстановить Монику

1

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

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

Я думаю, что есть много веских аргументов против нуля:

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

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

  • и т.д ...

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

Хорошие люди, которые написали язык, думали, что они будут кому-то полезны. Если вы понимаете возражения и по-прежнему думаете, что они вам подходят, больше никто не узнает.


Дело в том, что «универсальный совет» обычно не формулируется так «универсально», как утверждают люди. Если вы находите первоисточник таких советов, они обычно говорят о предостережениях, о которых знают опытные программисты. Случается так, что, когда кто-то еще читает совет, он, как правило, игнорирует эти предостережения и запоминает только часть «универсального совета», поэтому, когда они связывают этот совет с другими, он получается гораздо более «универсальным», чем предполагал автор. ,
Никол Болас

1
@NicolBolas, вы правы, но первоисточник - это не совет, который цитируют большинство людей, а переданное сообщение. Я хотел сказать лишь то, что многим программистам сказали: «Никогда не делай Х, Х - зло / плохо / что угодно», и, как ты говоришь, в нем пропущено множество предостережений. Может быть, они были отброшены, может быть, они никогда не присутствовали, конечный результат тот же.
Drjpizzle

0

У Java есть Optionalкласс с явным ifPresent+ лямбда для безопасного доступа к любому значению. Это можно сделать и в JS:

Смотрите этот вопрос StackOverflow .

Кстати, многим пуристам это использование покажется сомнительным, но я так не думаю. Дополнительные функции существуют.


Не может ли ссылка на java.lang.Optionalбыть nullтоже?
Дедупликатор

@Deduplicator Нет, Optional.of(null)выдаст исключение, Optional.ofNullable(null)выдаст Optional.empty()значение not-there.
Joop Eggen

А Optional<Type> a = null?
Дедупликатор

@Deduplicator true, но там вы должны использовать Optional<Optional<Type>> a = Optional.empty();;) - Другими словами, в JS (и других языках) такие вещи будут невозможны. Scala со значениями по умолчанию будет делать. Ява может быть с перечислением. Я сомневаюсь Optional<Type> a = Optional.empty();.
Joop Eggen

Итак, мы перешли от одного косвенного и потенциального nullили пустого к двум плюс некоторым дополнительным методам на вставленном объекте. Это настоящее достижение.
дедупликатор

0

CAR Hoare назвал нуль своей «ошибкой в ​​миллиард долларов». Тем не менее, важно отметить, что он думал, что решение было не «нигде не имеет значения null», а вместо этого «ненулевые указатели как значения по умолчанию, и null только там, где они семантически необходимы».

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

Чтобы обойти это фундаментальное отсутствие выразительности, некоторые сообщества (например, сообщество Scala) не используют нуль. В Scala, если вам нужно значение типа T, допускающее обнуляемость , вы берете аргумент типа Option[T], а если вам нужно значение, не допускающее обнуления, вы просто принимаете a T. Если кто-то пропустит нуль в ваш код, ваш код взорвется. Однако считается, что вызывающий код является ошибочным кодом. OptionЭто довольно хорошая замена для нуля, потому что общие шаблоны обработки нуля извлекаются в библиотечные функции, такие как .map(f)или .getOrElse(someDefault).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.