2015-06-08 4 views
14

Кажется, растет сообщество людей, говорящих, что вы никогда не должны возвращать нуль и всегда должны использовать шаблон Null Object Pattern. Я могу видеть полезность NOP при использовании коллекции/карты/массива или вызывать логические функции, такие как isAuthenticated(), which is shown here.Null Object Pattern

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

Я понимаю, что вместо возврата нулевого объекта вы возвращаете действительный объект, который был «обнулен».

Так, например, клиент будет сделать вызов, чтобы получить объект:

Car car = getCar(); 

Если не используется NOP вам нужно будет проверить, если объект, возвращаемый из getCar() равно нулю перед вызовом каких-либо методов на нем:

if (car != null){ 
    color = car.getColor(); 
    doScreenStuff(color); 
    } 

Использование NOP, вместо getCar() возвращающей нуль, теперь возвращает объект, который был фактически «обнуление». Так что теперь нам больше не нужно делать if (car != null) и можете просто запросить цвет. Таким образом, я полагаю, что наш объект «обнуленного» будет возвращать «нет», когда мы будем называть цвет.

Как это помогает? Кажется, что перемещение вперед и вызов методов на пустом объекте вызывает столько же боли, сколько просто проверка нуля. Теперь, когда пришло время отображать информацию, нам нужно проверить, что цвет не является «ничем», что высота не равна 0 или любым другим значениям, которые у вас есть. Поэтому, по сути, вместо проверки в начале обработки, если автомобиль имеет значение null, вы проверяете его, если у нас есть автомобиль или реальный автомобиль. И.Е. мы не хотим отображать кучу пустых объектов, поэтому нам нужно каким-то образом отфильтровать все наши пустые объекты.

Эта фильтрация представляет собой добавленный шаг, аналогичный вызову if (car! = Null). Единственное различие заключается в том, что при проверке null мы можем прекратить обработку, как только обнаружим, что объект автомобиля имеет значение null, бросая исключение, тогда как с NOP мы вызываем методы на пустой объект и продолжаем прерываться, пока не получится время отобразить объект, и в этот момент мы отфильтровываем опорожнения. Кроме того, вам нужно знать значения, возвращаемые вашим пустым объектом. И.Е. getColor() возвращает «none» или «empty».

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

ответ

6

Путь нулевого объекта имеет смысл только тогда, когда для нулевого объекта существует разумное функциональное значение. Цель состоит не в том, чтобы отложить нуль, как вы описали, но полностью исключить идею null, представляя пустоту или пустоту с фактической частью данных, которая все еще функционирует. Например, естественный случай дыр в древовидной структуре, as described in the Wikipedia article.

Нулевой автомобиль не имеет смысла. В этом случае кажется, что более подходящая вещь будет для getCar() для возврата Optional<Car>.

0

Объект Null Pointer Object, по крайней мере, по моему опыту, позволяет ограничить вас нулевыми проверками в одном центральном местоположении, чтобы исключить исключения из нулевого указателя.

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

Я больше не использую его. Существуют альтернативы использованию нулевых проверок, например, использование Java 8 Optional. Есть люди, которые за и против этого тоже, и это ни в коем случае не заменяет шаблон нулевого объекта.

String result = Optional.ofNullable(someInteger) 
    .map(Integer::valueOf) 
    .map(i -> i + 1) 
    .map(Object::toString) 
    .orElse("number not present"); 

System.out.println(result); 
+0

Путь нулевого объекта и, по желанию, не соответствует той же цели. Необязательный используется для указания того, что значение может быть или не быть. Шаблон нулевого объекта просто кодирует тот факт, что объект «ничего не существует», который все еще имеет полезное поведение. – Cubic

+0

Извините, я, возможно, не уточнил, я говорил, что альтернатива null проверкам является опцией, а затем «это ни в коем случае не замена шаблона нулевого объекта» для шаблона нулевого объекта. Я не использовал шаблон нулевого объекта некоторое время, потому что у меня не было недавнего варианта использования, но я ни в коем случае не предлагаю, чтобы опции заменяли шаблон нулевого объекта. –

3

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

Что касается этого конкретного шаблона, он предполагает определенный стиль программирования, который может быть неуместным для вас. Я бы никогда не использовал его сам, потому что возвращаю nulls как законные значения (отсутствующие данные), которые обрабатываются по-разному в каждом случае, поэтому «централизованная обработка» не имеет для меня никакого смысла. Когда я возвращаю логические значения, я использую примитивы.

Суть в том, что если шаблон кажется вам неестественным, не используйте его.

+0

Думаю, ты прав. Я всегда слишком беспокоюсь о неправильном использовании при программировании. В этом случае, похоже, есть люди, которые предлагают вам делать что-то совершенно неправильно, если вы возвращаете нули: http://www.yegor256.com/2014/05/13/why-null-is-bad.html – TigerBear

6

Ответ MattPutnam находится прямо на месте, и я его второй. Я бы добавил: понятие «нулевой объект», когда вы его анализируете, похоже, сводится к математической концепции monoid. Вы можете думать об этом так: моноиде типа, который имеет оба этих вещи:

  1. An «Append», «сумма» или аналогичную операцию, которая должна быть ассоциативным: a.op(b).op(c) одно и то же как a.op(b.op(c)).
  2. «Пустое», «нулевое» или «нулевое» значение, которое действует как нейтральный элемент или идентификационный элемент операции.

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

Проблема заключается в том, что Car не является моноидом; нет понятия «пустой автомобиль» или «нейтральный автомобиль», и нет действительно разумной операции, которую вы могли бы использовать для объединения двух Car s в один.

Итак, рекомендация, которую вы по праву получаете, - это использовать что-то вроде Java 8 Optional. И хитрость заключается в том, что независимо от того, какого типа T есть, Optional<T> не моноид:

  1. Моноид в «объединить» операция «выбрать первое значение, если это не empty, в противном случае выбрать второе значение»:
    • x || empty = x
    • empty || x = x
  2. нейтральный элемент Optional.empty(), потому что Optional.empty().orElse(anything) такая же, как только anything.

Так в основном, Optional<T> является оболочкой, которая добавляет нулевой объект для типов, таких как Car, которые не имеют один. Метод Optional<T>.orElse(T value), который является слегка реорганизованной версией «выбрать первое значение nonempty» моноида.

0

NULL design pattern заполняет ОТСУТСТВИЕ объекта с поведением ПО УМОЛЧАНИЮ и должен использоваться только в том случае, если один объект взаимодействует с другим.

NULL design pattern не предназначен для замены обработки исключений NULL. Это одно из побочных преимуществ шаблона проектирования NULL, но цель состоит в том, чтобы обеспечить поведение по умолчанию.

Например, рассмотрите приведенный ниже пример кода pseduo. Его простой класс Customer, который делегирует расчет скидки на объект со скидкой.

class Customer { 
    IDiscount dis = new NormalDiscount(); 
    public double CalculateDiscount() { 
     return dis.Calculate(); 
    } 
    // Code removed for simplicity 
} 

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

class DefaultedCustomer : Customer { 
    IDiscount dis = null; 
    public double CalculateDiscount() { 
     return dis.Calculate(); <---- This will crash in parent 
    } 
    // Code removed for simplicity 
} 

Таким образом, один из способов, по которым разработчики исправляют это, является моей проверкой на NULL и возвратом нуля. Если вы закрываете глаза и логически мыслите, это на самом деле бизнес-сценарий, где нет скидок для дефолтного клиента.

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

public class DefaultDiscount : IDiscount { 
    public void Calculate() { 
     return 0; 
    } 
} 

Нулевые значения необходимы, но для обработки исключений не для фиксации отсутствия объекта в сотрудничестве, как показано выше. Шаблон проектирования NULL не имеет смысла, если нет совместной работы.

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

Все вышеперечисленные мысли, которые я изложил из этого NULL Design pattern in C#, объясняет, что делает и делает NULL.

-1

Возврат Дополнительного автомобиля не отменяет шаг проверки, действительно ли объект автомобиля присутствует или нет.

При возвращении автомобиля, вы должны проверить, является ли (автомобиль! = Null). Аналогично при доступе к опциональному вам следует проверить, есть ли (car.isPresent()) car.get().

Выполнение get() без проверки присутствия неприемлемо, и его можно легко идентифицировать и избежать, используя конфигурацию checkstyle для выброса ошибок.

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

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

Он действует как программный способ документирования методов, которые внутренне помогают обеспечить его проверку с помощью checkstyles.

+0

Необязательный - это не то же самое, что и шаблон нулевого объекта (https://en.wikipedia.org/wiki/Null_Object_pattern). –

+0

Использование 'Optional' только для' isPresent() 'и' get() 'похоже только на 20 миль в час в вашем новый Lamborghini. Лучше всего в этой ситуации - getCar(). IfPresent (car -> doScreenStuff (car.getColor())) ' – Michael