null or not

Разгребал я недавно свои ссылки «на почитать», и всплыли 2 интересных:
goo.gl/P5Pf2
goo.gl/61yzt

Хотелось бы обсудить, что делать когда нечего возвращать из вычисления. Каждый раз проверять на null и игнорировать блоки и/или бросать исключения или же работать с optional type, благо реализации есть под многие языки, например для java в guava, для c++ — boost.

Посему вопрос: Как вы предпочитаете работать null/nil/none/etc? Какой подход считаете более правильным? Какой более удобным?

👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

IMHO, Optional следует рассматривать не как взятое понятие из guava, а из scala. Использование опциональных переменных удобно при проектировании системы, когда на уровне типов ты задаешь поведение. Я бы скорее рассматривал это нововведение как новый шаблон проектирования, который инкапсулирует проблему null.

Из действительно практичных аргументов в пользу использования Optional можно было бы назвать представление null-объекта в какой-нибудь гомоморфной иерархии. Или хотя бы там, где в базовом классе определены полезные методы.

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

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

Я стараюсь избегать вообще использования null. То есть если у меня есть метод который берет параметр который может быть нуллом, то я пишу 2 метода, один с параметром который никогда не нилл, а второй без параметра. Нуллов я считаю надо избегать. Не возвращать нулл на выходе, а бросать иксепшен или возвращать Optional по возможности. Желательно применять контракты, писать в джавадоке какие параметры нулловые, какие нет и применять да, гуаву для проверки входных условий.

Богдан, в мире Java nullability — очень философский вопрос. Мы вокруг этих nullов скачем, как хаскелисты вокруг своих монад :)

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

Вообще вопрос nullability, я считаю, overrated. На практике NPE встречается редко (и с опытом программиста становится все реже и реже), а когда все-таки вылетает, исправляется очень быстро. Обычно это:
— недоконфигурировали какую-то связку библиотек — встречается в основном на старте проекта, можно потерпеть недельку, потом null — уже не проблема
— куда-то что-то забыли передать — свидетельствует о неинтуитивном апи — если в нашем коде вылетает, то рефакторим, иначе просто терпим и корпим стороннее апи всем, что ему на кой-то черт надо

— неожиданно вернулся null с базы — часто свидетельствует о недостаточной проработке требований. Решаем с BA, как будем обрабатывать пустые значения и идем дальше. Часто BA придумывают сложные правила, и как раз из-за них в нашем коде возникает дофига проверок на null. Я решаю все пре-сондишинами и/или пост-кондишинами.

Есть оригинальные решения проблемы nullability: в одном проекте экспериментировал с тем, чтобы использовать Mockito для генерации null objects (опция return mocks очень помогает в chained calls). Оказалось не очень:
— во-первых, многие разработчики чувствуют себя неуютно в такой среде — код кажется незнакомым, многие баги — неочевидными, использование моков в «боевом» коде — идеологически неверным.
— о багах: простые баги с NPE заменились на сложно отслеживаемые баги с появлением моков в качестве настоящих сущностей, когда в какой-то момент не сделали проверку, является ли объект молом, и пропустили его дальше в бизнес-логику. По внутреннему ощущению такие баги похожи на NaN в чилсовых вычислений. Моки тоже заразные (метод, принимающий мок и нормальный объект с высокой вероятностью вернет мок), поэтому, когда нам встречается мок, отследить, откуда он к нам попал, очень трудно.

В общем, попробовали и решили, что не стоит овчинка выделки.

В целом мои выводы по поводу nullов:
— preconditions работают, требуют определенного внимания. Аннотации @Nullable. @NotNull и др. немного помогают, но не особо — сейчас их тоже не использую, т.к. в сочетании с прекондишинами получается масло масляное.
— null objects, собранные вручную — слишком тяжелы и требуют гораздо больше работы, чем приносят пользы. Используем их только для очевидных случаев (например, Collections.emptyList() для коллекций).

— автоматические null-объекты (моки) — слишком непредсказуемые, не используем.

тут врядли «или» подходит — єто разные подходы, для разных задач ...

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

тут врядли «или» подходит — єто разные подходы, для разных задач ...

Интересно как и когда бы вы применяли (если бы применяли) тот или другой подход.

ну наиболее «известная» вещь — это субд, иногда удобнее сделать field nullable иногда not null, иногда not null default ля-ля-ля

а так банальные вещи типа возвращения пустого массива ...

иногда удобнее сделать field nullable иногда not null,

Вот речь была про то когда удобнее одно, а когда другое.

пришлось задуматься. в общем так — если это достаточно просто, то постараюсь обойтись без null (например в базе создам запись «not set» и по дефолту привяжу по foreign key или там инициировать чет пустой коллекцией) , если это требует сложных делодвижений , то пойду через null

или же работать с optional type, благо реализации есть под многие языки, например для java в guava, для c++ — boost.

Я так понимаю основная проблема не в самом optional type(его даже самому накидать не проблема) а в том что бы его инфраструктура и либы языка правильно обрабатывали, типо что бы +, - и т.д. были монадами совместимыми с монадой option, а если такого нет как в джаве то нафиг-нафиг, только все усложнит.

Ну и трейсить тяжело если это явно не поддерживается, типа отследить в какой именно момент option стал None.

а если такого нет как в джаве то нафиг-нафиг, только все усложнит.

Усложнит. Но в некотором плане «x.isPresent()» понятнее чем «x != null». Из более весомых аргументов: docs.guava-libraries.googlecode.com/....base.Function

Такое может существенно упростить работу с опшионалом. Но это как бы завяжет весь код на его использование.

Но в некотором плане «x.isPresent()» понятнее чем «x != null».
Никогда не мог понять чем именно понятнее.

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

Можно сказать что нулл — это типа показатель того что у переменной вообще нет значения как такового.

А про isPresent что можно сказать?

Еще прикалывает раньше нужно было проверять переменные на null, а теперь на null и еще isPresent, покращення на лицо.

а теперь на null и еще isPresent, покращення на лицо.

Только на isPresent.

А где гарантия что переменная не null?

А где гарантия что переменная не null?

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

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

А проверка на нулл — это или ошибка, или он должен туда проходить.

Лично я, и как мне кажется создатели джава предпочитают использовать исключения для индикации ошибок.

Подписаться на комментарии