Null Object Pattern, чтобы избежать нулевых проверок?

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

например, предположим, что класс DAO возвращает информацию о клиенте (в объекте значения, называемом CustomerVO). Мой основной класс должен извлекать firstName и emailId и отправлять электронное письмо клиенту.

...
CustomerVO custVO = CustomerDAO.getCustomer(customerID);
if(custVO != null) { // imp, otherwise we may get null ptr exception in next line
     sendEmail(custVO.getFirstName(), custVO.getEmailID());
}
...

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

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

Хорошо, возвращаясь к шаблону нулевого объекта, можем ли мы использовать «нулевой» объект CustomerVO, чтобы скрыть нулевую проверку?

CustomerVO {
   String firstName = "";
   String emailID = ""; 
}

Не было бы смысла. Как вы думаете?

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


person Rohit    schedule 17.10.2010    source источник


Ответы (5)


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

Как вы сказали, многие новые разработчики тратят время, пытаясь защитить свой код от исключительных ситуаций, которые хуже, чем останавливающаяся программа.

person rerun    schedule 17.10.2010

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

Здесь шаблон нулевого объекта был бы действительно полезен, если бы класс CustomerVO реализовал метод sendEmail(). тогда вы можете просто связать вызовы вместе, поскольку контракт getCustomer() гарантирует, что ссылка null не будет возвращена:

CustomerDAO.getCustomer(customerID).sendEmail();

В этом случае метод sendEmail() проверит, что его попросили воздействовать на специальный «нулевой объект», и просто ничего не сделает (или что-то еще).

person Michael Burr    schedule 17.10.2010
comment
+1, хотя я бы не хотел, чтобы у моих клиентов были методы sendEmail. Это просто возлагает на класс клиентов слишком много обязанностей. Это похоже на то, как дать каждому классу метод печати и теперь нужно везде реализовывать логику печати или сделать каждый класс зависимым от некоторого диспетчера печати, в то время как он может быть ограничен диспетчером печати и формами/классами, которые фактически инициируют печать, предоставляя диспетчеру печати классы. печатать. Где нулевой шаблон действительно хорош, так это в модульном тестировании, например, для предоставления класса журнала, который ничего не делает, поэтому вам не нужно исправлять тестируемый код. - person Marjan Venema; 17.10.2010

Должен ли ваш метод getCustomer вызывать исключение, если клиент не найден, а не возвращать null?

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

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

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

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

person mtreit    schedule 17.10.2010
comment
Смотрите мой ответ, если что-то не может существовать, есть метод, который проверяет, существует ли это. Таким образом, вам нужно только генерировать исключения там, где это исключительно. - person nicodemus13; 15.05.2012

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

Метод CustomerVO custVO = CustomerDAO.getCustomer(customerID); делает две вещи.

Во-первых, он возвращает клиента, если он существует, и, во-вторых, возвращает ноль, если такого клиента нет. Это две разные операции, и их следует кодировать соответствующим образом.

Лучшим подходом будет:

bool customerExists = CustomerDAO.exists(customerID);

if (customerExists)
{
  CustomerVO custVO = CustomerDAO.getCustomer(customerID);
  sendEmail(custVO.getFirstName(), custVO.getEmailID());
}
else
{
   // Do whatever is appropriate if there is no such customer.
}

}

Итак, разделите метод на два, один из которых проверяет, существует ли запрошенный объект, а второй фактически извлекает его. Нет необходимости в каких-либо исключениях, а дизайн и семантика очень ясны (что, на мой взгляд, с шаблоном «не существует-возвращает-нуль» не так). Более того, в этом подходе метод CustomerDAO.getCustomer(customerID) выдает ArgumentException, если запрошенный клиент не существует. В конце концов, вы просили Customer, а его нет.

Кроме того, на мой взгляд, ни один метод не должен возвращать null. Null явно означает: «Я не знаю, какой правильный ответ, мне нечего возвращать». Null - это не смысл, это отсутствие смысла. Спросите себя: «Почему я возвращаю то, что, как я знаю, не должно происходить на самом деле?» Ваш метод GetCustomer должен, очевидно, возвращать Customer. Если вы возвращаете null, вы просто перекладываете ответственность за то, что делать, на цепочку вызовов и нарушаете контракт. Если есть значимое значение по умолчанию, используйте его, но исключение здесь может заставить вас задуматься о правильном дизайне.

person nicodemus13    schedule 15.05.2012
comment
Просто убедитесь, что вы проектируете потокобезопасность здесь. Первоначальный вызов exists() вполне может вернуть true, но последующий вызов getCustomer() может по-прежнему завершаться ошибкой, если, например, какой-то другой код удалил клиента между вызовами. Что-то вроде TryGet() — это шаблон, который пытается смягчить это. См. документы. .microsoft.com/en-us/dotnet/api/ для примера этой идеи. - person Adam Naylor; 14.09.2018

Имя является шаблоном проектирования NULL объекта, а не шаблоном проектирования NULL check. Нулевой объект означает отсутствие объекта. Это следует использовать, когда у вас есть объекты, работающие в СОТРУДНИЧЕСТВЕ. В вашем случае вы проверяете наличие объекта, для которого проверка NULL должна быть в порядке.

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

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

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

http://www.codeproject.com/Articles/1042674/NULL-Object-Design-Pattern

person Shivprasad Koirala    schedule 30.10.2015