Re[6]: два типа возвращаемых функцией
От: LeonidV Ниоткуда http://vygovskiy.com
Дата: 27.04.11 13:31
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, LeonidV, Вы писали:


M>Вопрос, что должен делать метод bark у NullDog? Лаять он не может, потому что на самом деле это не собака. Соответственно, LSP нарушен.

Почему не собака? NullDog это собака, которая лает "ничем", то есть молча. Брость исключение, ясное дело, нельзя — тут сразу нарушается контракт и смысл NullObject теряется.
http://jvmmemory.com — простой способ настройки JVM
Re[6]: два типа возвращаемых функцией
От: LeonidV Ниоткуда http://vygovskiy.com
Дата: 27.04.11 13:51
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>1) См. пункт про дисциплину. В реальности соглашениям следуют только в том случае, если они действительно упрощают жизнь.

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

NGG>Следовательно мечта ВООБЩЕ не иметь в коде проверок на особые значения — довольна наивна.

Есть такое. Но проверк можно делать через map.contains(key) : Boolean. Понятно, если contains делает запрос к БД, так лучше не делать и проще обработать null.

NGG>Подвох номер один в том, что null может иметь разную смысловую нагрузку:

NGG>- данных нет
NGG>- данные не известны

Я так понимаю, тут различие в том что пользователь при заполнение анкеты в графе "судимости:
— ставить нет // сохраняем false
— ничего не ставит //сохраняем true.

Тогда можно из БД считать null, а в модель поместить признак "судимостиУказаны: Boolean". Сам список судимостей будет, соответственно пустым. Если нужно просто вывести все анкеты — все работает отлично. Если, например, нужно получить статистику — запрашиваем признак. А если без nullobject, то проверку на null надо будет делать и при выводе.

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


NGG>Положа руку на сердце: если возникает необходимость узнать является объект нулевым или нет, идиома o != null/ o == null

NGG>явно в выйгрыше: нет проблем с наследованием, не нужно ничего изучать (эту идиому знает КАЖДЫЙ), что написано то и делается.
Проверка на null не показывает: нет данных или данные не известны.
http://jvmmemory.com — простой способ настройки JVM
Re[6]: два типа возвращаемых функцией
От: LeonidV Ниоткуда http://vygovskiy.com
Дата: 27.04.11 13:52
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>1) См. пункт про дисциплину. В реальности соглашениям следуют только в том случае, если они действительно упрощают жизнь.

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

NGG>Следовательно мечта ВООБЩЕ не иметь в коде проверок на особые значения — довольна наивна.

Есть такое. Но проверк можно делать через map.contains(key) : Boolean. Понятно, если contains делает запрос к БД, так лучше не делать и проще обработать null.

NGG>Подвох номер один в том, что null может иметь разную смысловую нагрузку:

NGG>- данных нет
NGG>- данные не известны

Я так понимаю, тут различие в том что пользователь при заполнение анкеты в графе "судимости:
— ставить нет // сохраняем false
— ничего не ставит //сохраняем null.

Тогда можно из БД считать null, а в модель поместить признак "судимостиУказаны: Boolean". Сам список судимостей будет, соответственно пустым. Если нужно просто вывести все анкеты — все работает отлично. Если, например, нужно получить статистику — запрашиваем признак. А если без nullobject, то проверку на null надо будет делать и при выводе.

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


NGG>Положа руку на сердце: если возникает необходимость узнать является объект нулевым или нет, идиома o != null/ o == null

NGG>явно в выйгрыше: нет проблем с наследованием, не нужно ничего изучать (эту идиому знает КАЖДЫЙ), что написано то и делается.
Проверка на null не показывает: нет данных или данные не известны.
http://jvmmemory.com — простой способ настройки JVM
Re[6]: два типа возвращаемых функцией
От: Ziaw Россия  
Дата: 27.04.11 19:46
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>>>"-": в куче случаев обычная проверка на null превращается в монстров

NGG>>> ( o instanceof NullBlaBla или o == BlaBla.NULL или BlaBla.isNull() или ... — и в каждом конкретном случае нужно залезть по глубже, чтобы узнать как правильно...)

Z>>Это решается одним соглашением. Не надо каждого для каждого конкретного случая придумывать свою реализацию. А обычных проверок на null должно остаться очень мало.


NGG>1) См. пункт про дисциплину. В реальности соглашениям следуют только в том случае, если они действительно упрощают жизнь.


ОМГ, этож какая дисциплина должна быть, если команда не может следовать единму стилю в паттерне null object . Может стоит запретить все сложнее if и goto?

NGG>2) Из перечисленных вариантов нельзя выбрать один "правильный".

NGG> — BlaBla.NULL не дружит с наследованием от BlaBla, зато довольно эффективен с точки зрения производительности.
NGG> — o instanceof NullBlaBla — решает (ли ?) проблему с наследованием, но медленный и трудно набираемый (да, программисты ленивые)
NGG> — BlaBla.isNull() — избавляет от необходимости писать сложные конструкции проверки, краток, но... костыль он и есть костыль.

Либо третий вариант, либо первый, не пойму почему первый не дружит.

NGG>Положа руку на сердце: если возникает необходимость узнать является объект нулевым или нет, идиома o != null/ o == null

NGG>явно в выйгрыше: нет проблем с наследованием, не нужно ничего изучать (эту идиому знает КАЖДЫЙ), что написано то и делается.

Согласен.

NGG>Естественно возникает вопрос: а есть ли нужда в таких проверках?


NGG>Достаточно посмотреть на языки, где понятия null просто нет. И сразу увидим, что там используется идиома MayBe (или Option), если функция/метод может вернуть, а может и не вернуть значение.

NGG>Заметьте — type (MayBe a) != type (a) и
NGG>система типов ФОРСИРУЕТ необходимость проверки было ли возвращено реальное значение или нет.
NGG>И если какая-то ветка не была обработана будет исключение времени выполнения. Т.е. тот же NPE по сути.

Именно, поэтому там где этого нет приходится придумывать костыли вроде NO.

NGG>Следовательно мечта ВООБЩЕ не иметь в коде проверок на особые значения — довольна наивна.


Блин =) Еще раз озвучу свой поинт. Этот паттерн подходит в тех случаях, где он подходит. Там где нужны проверки — он не подходит. Простейшие случаи применения я уже описал — возвращаем пустую строку либо пустой массив вместо null.

NGG>Подвох номер один в том, что null может иметь разную смысловую нагрузку:

NGG>- данных нет
NGG>- данные не известны

NGG>Значит обработка этих случаев в общем случае должна быть различной.

NGG>Обычно вариант обработки выводится из контекста и факта получения null.

Обычно в таких случаях нафик не нужен null object. Нужна сигнализация, что данных нет (если это доставание из базы по ключу, обычно подходит банальный экзепшн).

NGG>Как вы этого добъётесь, если у вас вместо null всегда NullBlaBla и делать проверку на null вы принципиально не желаете?

NGG>(кстати, а NullBlaBla.equals(NullBlaBla) должен возвращать тру или фалсе? — рекомендую обратиться к дейту и его мыслям относительно null-значениях в базах данных).



NGG>Н-р,

NGG>
NGG>// fragment 1
NGG>   BlaBla b = ...
NGG>   if (b == null) 
NGG>   {
NGG>      ... show user dialog ...
NGG>   }
NGG>   else 
NGG>   {
NGG>       b.foo();
NGG>   }

NGG>// fragment 2
NGG>   BlaBla b = ...
NGG>   if (b == null) 
NGG>   {
NGG>      ... do nothing, it's ok in this context ...
NGG>   }
NGG>   else 
NGG>   {
NGG>      b.foo();
NGG>   }
NGG>


NGG>Что должно делать NullBlaBla.foo(), что бы удовлетворить всех желающих?

NGG>Придётся ввести контекст (fragment1/fragment2) в качестве параметра в foo(), т.е. поменять интерфейс BlaBla...

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

NGG>Беее... и так для каждой проверки на null в коде.

NGG>... Или вынести зависимость от контекста в блоки "b = ...", но тогда у нас появится NullBlaBla1 и NullBlaBla2.
NGG>Столько мороки и не очевидностей в коде из-за желания не делать проверку на null — стоит ли оно того?

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

NGG>Подвох номер два:

NGG>
NGG>// fragment 1
NGG>  Order o = ...
NGG>  if (o == null || o.isBuy()) {
NGG>     doBuy();
NGG>  } else {
NGG>     doSell();
NGG>  }

NGG>// fragment 2
NGG>  Order o = ...
NGG>  if (o == null || !o.isBuy()) {
NGG>     doSell();
NGG>  } else {
NGG>     doBuy();
NGG>  }
NGG>

NGG>Э... NullOrder.isBuy() должен возвращать true или false, чтобы оба фрагмента продолжили корректно работать?
NGG>Суть в том, что значение isBuy() не определено для NullOrder, а поэтому никакое дефолтное значение не будет правильным в общем случае.

Снова использование null в качестве сигнализации спец-состояния вместо возврата значения. Мне этот подход не нравится, но иногда он удобнее всего остального.

Z>>А не надо использовать null вообще как значение для этого типа. Возникнуть он может в очень узком классе мест, их легко локализовать.


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


Мы сейчас про розги или про то, как сделать код лучше? Это разные понятия.

Z>>Зачем его пропагандировать? Это просто один из паттернов. И как любой паттерн применяется там, где в нем есть необходимость, какая тут может быть пропаганда?


NGG>Вот и я думаю, зачем? Иногда полезен, чаще — нет.


NGG>В тоже время в этом обсуждении звучат такие лозунги:

NGG>"Суть в том, чтобы null'ов или вообще не было в программе, или они были изолированы в DAL (там null возвращается из БД)."

Это другая крайность, но если люди могут ее достичь, я могу только аплодировать.
Re[4]: два типа возвращаемых функцией
От: Ziaw Россия  
Дата: 27.04.11 19:58
Оценка:
Здравствуйте, Undying, Вы писали:

U>А кроме коллекций (string это по сути тоже коллекция) можно примеры привести, где выполняется следующее?


Z>>В том то и фишка паттерна, что проверять не надо, код и так ведет себя с ними так, как требуется.


То есть пользу для коллекций ты признаешь?

Я не буду трогать объекты предметной области (хотя Фаулер как раз для них рекомендует). Просто на каждый частный случай можно придумать сценарий, где НО покажет себя не очень. Несмотря на это, я считаю, что этих частных случаев неожиданно возникает на порядки меньше чем потенциальных NRE от применения null в качестве многоликого спец-кода возврата (ничего не нашел, пользователь не знает чего хочет). Единственный правильный нулл — поле в базе не заполнено.

В качестве примеров, кроме коллекций, могу предложить объект конфигурации (если не найден возвращается дефолтный). Логгер который ничего никуда на самом деле не пишет. Функция которая ничего не делает (реально часто использую).
Re[7]: два типа возвращаемых функцией
От: NotGonnaGetUs  
Дата: 28.04.11 08:18
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>ОМГ, этож какая дисциплина должна быть, если команда не может следовать единму стилю в паттерне null object . Может стоит запретить все сложнее if и goto?


Goto тоже нужно запретить! Только для этого тоже нужна строгая дисциплина

NGG>> — BlaBla.NULL не дружит с наследованием от BlaBla, зато довольно эффективен с точки зрения производительности.

Z>Либо третий вариант, либо первый, не пойму почему первый не дружит.

BlaBla = List.

Чем должно быть List.NULL?
Не изменяемым списком нулевой длинны? А если список изменяемый (ArrayList,LinkedList)? Пользовательский код расчитан на возможность модификации списка, а не тут то было. И т.п.


NGG>>Естественно возникает вопрос: а есть ли нужда в таких проверках?


NGG>>Достаточно посмотреть на языки, где понятия null просто нет. И сразу увидим, что там используется идиома MayBe (или Option), если функция/метод может вернуть, а может и не вернуть значение.

NGG>>Заметьте — type (MayBe a) != type (a) и
NGG>>система типов ФОРСИРУЕТ необходимость проверки было ли возвращено реальное значение или нет.
NGG>>И если какая-то ветка не была обработана будет исключение времени выполнения. Т.е. тот же NPE по сути.

Z>Именно, поэтому там где этого нет приходится придумывать костыли вроде NO.


Так эти костыли не заменяют MayBe, т.к. type NO == type O и следовательно нет никакого форсирования проверки результата/входного параметра. В тоже время от null никуда не денешься. Как минимум ошибка программиста всплывёт в том месте, где она случилась, а не через 10 уровней по стеку.

NGG>>Следовательно мечта ВООБЩЕ не иметь в коде проверок на особые значения — довольна наивна.


Z>Блин =) Еще раз озвучу свой поинт. Этот паттерн подходит в тех случаях, где он подходит. Там где нужны проверки — он не подходит. Простейшие случаи применения я уже описал — возвращаем пустую строку либо пустой массив вместо null.


Во-во

А я ещё раз озвучу мой поинт:

"А в реальности область применения ещё уже, чем кажется."

Упрощая при помощи NO частые случаи проверки на null, приходится расширять спецификацию класса (специальным значением null) и значит при работе в коде с этим классом нужно всегда задавать себе вопрос: а будет ли этот код корректно работать с таким инстансом.

Н-р, в классе Double есть спец. значения NaN/Inf. Остаётся только благодарить бога, большая часть кода с ними не вызывается

Z>Обычно в таких случаях нафик не нужен null object. Нужна сигнализация, что данных нет (если это доставание из базы по ключу, обычно подходит банальный экзепшн).


Труъ. И очень многих других


NGG>>Что должно делать NullBlaBla.foo(), что бы удовлетворить всех желающих?

NGG>>Придётся ввести контекст (fragment1/fragment2) в качестве параметра в foo(), т.е. поменять интерфейс BlaBla...

Z>Хотел сразу поругаться на код, но лучше просто попрошу раскрыть многоточия в инициализации. Причем так, чтобы было понятно, что и для чего берется.


Этот код не нужно ругать, он просто служит для иллюстрации того, что способ обработки null в разных контекстах может различаться, а поэтому установка разумного поведения методов NullObject'ов в _общем_ случае затруднена.

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


Z>Я не очень понимаю с чем идет спор. Я не противник этих проверок (в языках без option type). Просто есть места, где их можно элегантно обойти с помощью NO. Не вижу ни одного повода не использовать этот паттерн там где он уместен.


Пример из жизни.

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

В итоге NO призванный избавить от NPE, только послужил его причиной — парадокс
+ времени на ругань было потрачено больше, чем потроебовалось бы на написание 256 проверок на null.

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


Z>Мы сейчас про розги или про то, как сделать код лучше? Это разные понятия.


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


NGG>>В тоже время в этом обсуждении звучат такие лозунги:

NGG>>"Суть в том, чтобы null'ов или вообще не было в программе, или они были изолированы в DAL (там null возвращается из БД)."

Z>Это другая крайность, но если люди могут ее достичь, я могу только аплодировать.


Главное, что бы затраты ресурсов на достижение этой цели не оказались слишком высоки.
Re[5]: два типа возвращаемых функцией
От: Undying Россия  
Дата: 28.04.11 11:33
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>То есть пользу для коллекций ты признаешь?


Да. Но топикстартер, насколько я понял, говорил об использовании NullablePattern'а в любых случаях.

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

Если же новая сущность появляется, например, у объекта есть некий идентификатор, который у NullableObject требуется выставлять в какое-то особое значение, то NullablePattern резко ухудшит качество кода. Т.к. на это специальное значение все равно придется делать проверки (пусть не везде), а такая проверка неочевидна, нестандартна и не контролируется рантаймом в отличии от проверки на банальный null.

Z>Я не буду трогать объекты предметной области (хотя Фаулер как раз для них рекомендует).


Как правило попытка использовать NullablePattern для бизнес-объектов приводит к появлению особых значений, поэтому Фаулер дает вредные советы.
Re[9]: два типа возвращаемых функцией
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.05.11 15:12
Оценка:
Здравствуйте, Sorc17, Вы писали:

S>
S>class NullDog ... {
S>    @override
S>    void bark() {
S>        throw new DogException("Собаки не существует.");
S>    }
S>}
S>

И чем это принципиально отличается от null? На всякий случай напомню, что null.bark() всегда выкинет нам NRE — полный аналог сообщения "собаки не существует". Вот только он делает это бесплатно — не нужно реализовывать этот bark вручную.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: два типа возвращаемых функцией
От: -VaS- Россия vaskir.blogspot.com
Дата: 16.05.11 17:47
Оценка:
TOM>И хочу спросить (удостовериться): правильно я рассуждаю или нет? Хорошо ли активно пользоваться тем же принципом, который лежит в основе null object pattern'a, и по пути минимизировать получение каких-либо null'ов?

А еще можно сокращать количество query-методов — проблема возвращения null исчезает сама собой.
Re[2]: два типа возвращаемых функцией
От: TheOldMan  
Дата: 17.05.11 00:24
Оценка:
Здравствуйте, -VaS-, Вы писали:

VS>А еще можно сокращать количество query-методов — проблема возвращения null исчезает сама собой.


Query-методы — это методы, которые возвращают какой-либо результат? Просто передаем методам объекты и методы модифицируют их?
суть в простоте, а простота в сути
Re[3]: два типа возвращаемых функцией
От: -VaS- Россия vaskir.blogspot.com
Дата: 19.05.11 08:44
Оценка:
TOM>Query-методы — это методы, которые возвращают какой-либо результат?

Да.

TOM>Просто передаем методам объекты и методы модифицируют их?


Можно и так, только главное не скатиться к фактической эмуляции query (типа void GetOrders(IList<Order> result))

Главное — не вытягивать из объекта данные (ask), а говорить ему (tell), что нужно сделать. Конечно, это в основном применимо только для бизнес-логики.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.