null or not
Разгребал я недавно свои ссылки «на почитать», и всплыли 2 интересных:
goo.gl/P5Pf2
goo.gl/61yzt
Хотелось бы обсудить, что делать когда нечего возвращать из вычисления. Каждый раз проверять на null и игнорировать блоки и/или бросать исключения или же работать с optional type, благо реализации есть под многие языки, например для java в guava, для c++ — boost.
Посему вопрос: Как вы предпочитаете работать null/nil/none/etc? Какой подход считаете более правильным? Какой более удобным?
20 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівIMHO, Optional следует рассматривать не как взятое понятие из guava, а из scala. Использование опциональных переменных удобно при проектировании системы, когда на уровне типов ты задаешь поведение. Я бы скорее рассматривал это нововведение как новый шаблон проектирования, который инкапсулирует проблему null.
Остальные причины, такие как читаемость и защита от дурака, приводимые в доке по guava, мне кажутся субъективными и спорными.
Да, хорошая вещь
Богдан, в мире 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 ля-ля-ля
а так банальные вещи типа возвращения пустого массива ...
Вот речь была про то когда удобнее одно, а когда другое.
пришлось задуматься. в общем так — если это достаточно просто, то постараюсь обойтись без null (например в базе создам запись «not set» и по дефолту привяжу по foreign key или там инициировать чет пустой коллекцией) , если это требует сложных делодвижений , то пойду через null
Я так понимаю основная проблема не в самом optional type(его даже самому накидать не проблема) а в том что бы его инфраструктура и либы языка правильно обрабатывали, типо что бы +, - и т.д. были монадами совместимыми с монадой option, а если такого нет как в джаве то нафиг-нафиг, только все усложнит.
Ну и трейсить тяжело если это явно не поддерживается, типа отследить в какой именно момент option стал None.
Такое может существенно упростить работу с опшионалом. Но это как бы завяжет весь код на его использование.
А про isPresent что можно сказать?
Еще прикалывает раньше нужно было проверять переменные на null, а теперь на null и еще isPresent, покращення на лицо.
Только на isPresent.
А где гарантия что переменная не null?
Если опшинал-переменная таки нулл, то это явная ошибка и ее надо чинить.
А проверка на нулл — это или ошибка, или он должен туда проходить. Не известно.
С другой стороны это решается аннотациями (Нуллебл/НотНулл).
В общем-то вижу только 1 недостаток у опшионалов (в контексте джава) — это то что коб может (к сожалению не было еще достаточно изолированного куска где я бы мог попробовать этот подход) стать громоздким.
Лично я, и как мне кажется создатели джава предпочитают использовать исключения для индикации ошибок.