Null Object Pattern, чтобы избежать проверок Null?
В последнее время я столкнулся с шаблоном проектирования Null Object, и мои коллеги говорят, что его можно использовать, чтобы покончить с проверками нулевого указателя, которые встречаются во всем коде.
Например, предположим, что класс DAO возвращает информацию о Customer (в объекте значения с именем 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());
}
...
Это очень простой пример, но такие нулевые проверки могут быстро распространиться по всему коду в зависимости от сложности объектов-значений.
У меня есть две проблемы с нулевой проверкой - они имеют тенденцию делать код уродливым и трудным для чтения - менее опытные разработчики ставят ненужные нулевые проверки, когда на самом деле они должны генерировать исключения. например, в приведенном выше коде было бы лучше выбросить исключение из самого getCustomer(), потому что, если он не может найти информацию о клиенте для данного CustID, это указывает на то, что CustID был недействительным.
Хорошо, возвращаясь к шаблону нулевого объекта, можем ли мы использовать нулевой объект CustomerVO, чтобы скрыть проверку нуля?
CustomerVO {
String firstName = "";
String emailID = "";
}
Не имеет смысла. что ты думаешь?
И что вы делаете, чтобы минимизировать нулевые проверки в вашем приложении.
5 ответов
В этом случае нулевой объект может быть неуместным, поскольку значение по умолчанию может повлиять на скрытие того, что в действительности является исключением. Если вы обнаружите, что вам нужно проверить, есть ли у вас безопасный нуль для выполнения какой-либо другой деятельности, то шаблон нуль ничего вам не даст.
Как вы сказали, многие новые разработчики тратят время на то, чтобы защитить свой код от исключительных ситуаций, которые хуже, чем остановка программы.
В то время как шаблон нулевого объекта имеет свое применение, вам все равно нужно будет выполнить проверку здесь, в противном случае вы попытаетесь sendEmail()
на адрес электронной почты, который является пустой строкой (или вы нажимаете чек в sendEmail()
который может так же легко проверить null
).
Где образец нулевого объекта был бы действительно полезен здесь, если CustomerVO
класс реализовал sendEmail()
метод. тогда вы можете просто объединить вызовы, так как контракт getCustomer()
будет гарантировать, что null
ссылка не будет возвращена:
CustomerDAO.getCustomer(customerID).sendEmail();
В этом случае sendEmail()
Метод будет проверять, что его попросили воздействовать на специальный "нулевой объект" и просто ничего не делать (или делать то, что подходит).
У меня есть проблема с этим типом кода, который является общим шаблоном. Вам вообще не нужны нулевые проверки, если вы разлагаете то, что фактически делаете. Проблема здесь, на мой взгляд, в том, что вы нарушаете 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
вы просто взваливаете на себя ответственность за то, что нужно сделать обратно на цепочку вызовов и нарушаете контракт. Если есть значимое значение по умолчанию, используйте его, но выбрасывание исключения может заставить вас задуматься о правильном дизайне.
Если ваш getCustomer
метод сгенерировать исключение, если клиент не найден, вместо возврата нуля?
Ответ, конечно же, заключается в том, что это зависит: почти никогда не бывает так, что идентификатор клиента не существует? Другими словами, это исключительное обстоятельство? Если так, исключение уместно.
Тем не менее, на уровне доступа к данным часто вполне нормально, когда чего-то не существует. В этом случае лучше не бросать, так как это не неожиданное, исключительное обстоятельство.
"Возвращать ненулевой объект с пустыми полями", вероятно, не лучше. Как вы узнаете, является ли возвращенный объект "действительным" или нет, без добавления теперь некоторого проверочного кода, который, вероятно, хуже, чем нулевая проверка?
Так что, если это может быть нормальное состояние, что что-то извлекается, не существует, шаблон проверки нуля, вероятно, лучше. Если это что-то неожиданное, тогда метод доступа к данным вызывает исключение NotFound.
Имя - это шаблон проектирования объекта NULL, а не шаблон проектирования проверки NULL. Нулевой объект означает отсутствие объекта. Это следует использовать, когда у вас есть объекты, работающие в СОТРУДНИЧЕСТВЕ. В вашем случае вы проверяете существование объекта, для которого должна выполняться проверка NULL.
Шаблон проектирования NULL не предназначен для замены обработки исключений NULL. Это одно из побочных преимуществ шаблона проектирования NULL, но целью является обеспечение поведения по умолчанию.
Проверки NULL не следует заменять объектами шаблона проектирования NULL, поскольку это может привести к незаметным дефектам в приложении.
См. Статью ниже, в которой подробно рассматриваются шаблоны проектирования DO и Donts of NULL.
http://www.codeproject.com/Articles/1042674/NULL-Object-Design-Pattern