空对象模式以避免空检查?

时间:2010-10-17 05:53:28

标签: design-patterns null-object-pattern

最近我遇到了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());
}
...

这是一个非常简单的示例,但是这样的空检查可以根据值对象的复杂性快速传播到整个代码中。

我有两个null检查问题,   - 他们倾向于使代码丑陋且难以阅读     - 经验较少的开发人员进行不必要的空检查,实际上他们应该抛出异常。例如在上面的代码中,最好从getCustomer()本身抛出异常,因为如果它无法找到给定CustID的客户信息,则表明CustID无效。

好的,回到null对象模式,我们可以使用'null'CustomerVO对象隐藏空检查吗?

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

不要有意义。你觉得怎么样?

为了最大限度地减少应用中的空值检查,您遵循了哪些内容?

5 个答案:

答案 0 :(得分:3)

虽然空对象模式已经使用了,但您仍然需要在这里进行检查,否则您将尝试sendEmail()到一个空字符串的电子邮件地址(或者您推送检查sendEmail(),这可以轻松检查null)。

如果CustomerVO类实现了sendEmail()方法,那么空对象模式在这里真的很有用。然后你可以简单地将调用链接在一起,因为getCustomer()的契约将确保不会返回null引用:

CustomerDAO.getCustomer(customerID).sendEmail();

在这种情况下,sendEmail()方法会检查它是否被要求对特殊的“空对象”采取行动,而不做任何事情(或任何适当的事情)。

答案 1 :(得分:2)

在这种情况下,null对象可能是不合适的,因为默认值可能实际上隐藏了实际的异常。如果您发现自己必须检查是否有安全null来执行其他活动,则null模式不会为您购买任何东西。

正如您所说,许多新开发人员花时间尝试保护他们的代码免受异常情况的影响,而这种情况比停止程序更糟糕。

答案 2 :(得分:1)

如果找不到客户,您的getCustomer方法是否会抛出异常,而不是返回null?

答案当然是,它取决于:几乎从不是否存在客户ID?换句话说,这是一个特例吗?如果是这样,则例外是恰当的。

但是,在数据访问层中,某些东西不存在通常是很正常的。在这种情况下,最好不要抛出,因为它不是一个意想不到的特殊情况。

'返回一个具有空字段的非null对象'可能没有任何好处。如果没有添加一些可能比空检查更差的检查代码,你怎么知道返回的对象是否“有效”?

因此,如果不存在被提取的内容可能是正常状态,则空检查模式可能是最佳的。如果它是意外的,那么让数据访问方法抛出NotFound异常可能会更好。

答案 3 :(得分:1)

我对这种类型的代码有一个问题,这是一种常见的模式。如果你分解你实际做的事情,你根本不需要空检查。在我看来,这里的问题是你违反了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.
}

}

因此,将方法拆分为两个,一个检查请求的对象是否存在,另一个检查实际检索它。不需要任何异常,并且设计和语义非常清楚(在我看来,它不是一个不存在的返回null模式)。此外,在此方法中,如果请求的客户不存在,CustomerDAO.getCustomer(customerID)方法将抛出ArgumentException。毕竟,你已经要求Customer并且没有一个。

此外,在我看来,没有方法应该返回nullNull明确地说,'我不知道正确的答案是什么,我没有回报的价值'。 Null不是意义,它是缺乏的含义。问问自己,“为什么我要归还一些我知道不应该发生的事情?您的方法GetCustomer应该明显返回Customer。如果您返回null,那么您只是负责支持如何备份调用链并违反合同。如果有有意义的默认值,请使用它,但在此处抛出异常可能会让您对正确的设计更加困难。

答案 4 :(得分:0)

名称是NULL对象设计模式而不是NULL检查设计模式。空对象表示没有对象。当您在COLLABORATION中使用对象时,应该使用此方法。在您的情况下,您正在检查是否存在对其进行NULL检查的对象。

NULL设计模式并不意味着替换NULL异常处理。这是NULL设计模式的附带好处之一,但目的是提供默认行为。

不应将NULL检查替换为NULL设计模式对象,因为它可能导致应用程序中出现静默缺陷。

请参阅下面的文章,其中详细介绍了DO设计模式的DO和Donts。

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