Design patterns 空对象模式以避免空检查?
最近我遇到了空对象设计模式,我的同事说它可以用来消除代码中遇到的空指针检查 例如,假设一个DAO类返回有关Customer的信息(在一个名为CustomerVO的值对象中)。我的主类应该提取firstName和emailId,并向客户发送电子邮件Design patterns 空对象模式以避免空检查?,design-patterns,null-object-pattern,Design Patterns,Null Object Pattern,最近我遇到了空对象设计模式,我的同事说它可以用来消除代码中遇到的空指针检查 例如,假设一个DAO类返回有关Customer的信息(在一个名为CustomerVO的值对象中)。我的主类应该提取firstName和emailId,并向客户发送电子邮件 ... CustomerVO custVO = CustomerDAO.getCustomer(customerID); if(custVO != null) { // imp, otherwise we may get null ptr except
...
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 = "";
}
别担心,这是有道理的。你觉得怎么样
为了最小化应用程序中的空检查,您采取了哪些措施 在这种情况下,空对象可能不合适,因为默认值实际上可能隐藏了实际上是异常的内容。如果您发现自己必须检查您的safe null是否可以执行其他活动,那么null模式不会给您带来任何好处
正如您所说的,许多新开发人员花时间试图保护他们的代码免受比停止的程序更糟糕的异常情况的影响 虽然空对象模式有它的用途,但是您仍然需要在此处进行检查,否则您将尝试将
sendmail()
发送到一个空字符串的电子邮件地址(或者将检查推入sendmail()
,这可以很容易地检查null
)
如果CustomerVO
类实现了sendmail()
方法,则null对象模式在这里将非常有用。然后您可以简单地将调用链接在一起,因为getCustomer()
的契约将确保不会返回null
引用:
CustomerDAO.getCustomer(customerID).sendEmail();
在这种情况下,
sendmail()
方法将检查它是否被要求对特殊的“null对象”执行操作,而只是不执行任何操作(或任何适当的操作)。如果找不到客户,您的getCustomer
方法是否应该抛出异常,而不是返回null
当然,答案是这要视情况而定:难道客户ID几乎永远不存在吗?换句话说,这是一个例外情况吗?如果是这样的话,例外情况是适当的
然而,在数据访问层中,某些东西不存在通常是很正常的。在这种情况下,最好不要扔,因为这不是意外的、特殊的情况
“返回一个具有空字段的非空对象”可能没有更好的效果。如果现在不添加一些可能比空检查更糟糕的检查代码,您如何知道返回的对象是否“有效”
CustomerVO {
String firstName = "";
String emailID = "";
}
因此,如果可能是一种正常状态,即正在获取的内容不存在,那么空检查模式可能是最好的。如果是意外情况,那么让数据访问方法抛出NotFound异常可能会更好。我对这种代码有一个问题,这是一种常见的模式。如果分解实际执行的操作,则根本不需要空检查。在我看来,这里的问题是你违反了SRP 方法
CustomerVO custVO=CustomerDAO.getCustomer(customerID)代码>做两件事
首先,如果客户存在,则返回客户;其次,如果没有客户,则返回null。这是两个不同的操作,应按此进行编码
更好的办法是:
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
。毕竟,你要的是一个客户
,却没有
此外,在我看来,任何方法都不应该返回null
Null
明确表示“我不知道正确答案是什么,我没有返回值”Null
不是意义,而是缺乏意义。问问自己,‘为什么我要退回一些我知道不应该发生的事情?显然,您的方法GetCustomer
应该返回Customer
。如果您返回null
,那么您只是在把该做什么的责任推到调用链的后面,并破坏了合同。如果存在有意义的默认值,请使用它,但是在这里抛出异常可能会让您更难思考正确的设计是什么。名称是空对象设计模式,而不是空检查设计模式。空对象表示没有对象。当对象协同工作时,应使用此选项。在您的例子中,您正在检查一个对象的存在性,对于该对象,空检查应该可以
空设计模式并不意味着要替换空异常处理。这是空设计模式的一个附带好处,但其目的是提供默认行为
空检查不应替换为空设计模式对象,因为它可能导致应用程序中的无提示缺陷
请参阅下面的文章,其中详细介绍了NULL设计模式的Do's和Donts
+1,但我不希望我的客户使用sendEmail方法。这只是给了客户太多的责任