Подтвердить что ты не робот

Нулевой шаблон объекта

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

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

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

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

Car car = getCar();

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

  • "добавление", "сумма" или аналогичная операция, которая должна быть ассоциативной: a.op(b).op(c) такая же, как a.op(b.op(c)).
  • "пустое", "нулевое" или "нулевое" значение, которое действует как нейтральный элемент или элемент идентификации операции.

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

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

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

  • Операция моноида "комбинировать" - это "выбрать первое значение, если оно не empty, в противном случае выбрать второе значение":
    • x || empty = x
    • empty || x = x
  • Нейтральный элемент Optional.empty(), потому что Optional.empty().orElse(anything) совпадает с anything.

В принципе, Optional<T> - это оболочка, которая добавляет нулевой объект к типам типа Car, у которых их нет. Метод Optional<T>.orElse(T value), который является слегка реорганизованной версией "выбрать первое не-t28 > значение" моноида.

Ответ 3

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

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

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

Ответ 4

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

Если много услуг использует 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);

Ответ 5

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

Шаблон проектирования NULL не предназначен для замены обработки исключений 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 в С#, который объясняет Do and Donts of NULL.

Ответ 6

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

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

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

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

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

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