在Uncle Bob解释如何处理书清洁代码中的Null对象时感到困惑

时间:2011-06-16 12:40:46

标签: coding-style

我今天正在阅读Uncle Bob关于异常处理的书,我可以从处理空值中回忆起那些方法不应该处理空值,因为它会使代码混乱。我有点困惑。     我一直认为一个方法应该始终确保它的依赖关系不是null(除非它们在构造函数和构造函数中注入了可空性)。 例如,如果我有方法

public void SendMessage(IEmailSender emailSender, contactList list)
{
    if(emailSender == null)
    {
         throw new ArgumentNullException("Failed to send  
                message.",MethodBase.GetCurrentMethod().GetParameters[0].Name);
    }
    if(list == null)
    {
         throw new ArgumentNullException("Failed to send  
                message.",MethodBase.GetCurrentMethod().GetParameters[1].Name);
    }

    // rest of code goes here

}

我错过了什么吗?

3 个答案:

答案 0 :(得分:3)

我还没有看过这本书,但我只能想象Bob叔叔主张使用Null Object Pattern而不是显式空引用处理。

例如,

,而不是

if(log != null)
  log.Write("My log message");

您可以创建一个包含ILogger方法的Write接口,并创建两个实现此接口的类:NullLoggerFileLogger。对于NullLogger方法的实现,Write将有一个空主体。

在我看来,这与您在示例中的明确的前置条件验证不同

答案 1 :(得分:3)

有两种观点:
一方面,通过调用您的方法,您可以通过自己的方法告诉来电者他究竟做错了什么。这对调用者来说非常好,因为他可以在他获得异常时立即修复它。如果你编写第三方使用的API代码,这将是真实有效的。

另一方面,如果你自己调用你的方法,抛出异常的合理参数是,因为你希望能够在你的调用代码中使用catch块来处理这种情况!如果你没有理由在某个地方处理它,为什么要抛出异常呢?我看到的唯一原因是,通过在GlobalExceptionHandler中捕获这些异常来进行详细的错误记录。

所以你看到我们在这里有两类异常:一个用于开发人员,避免错误使用API​​,另一个用作方法的错误结果。

如果您正在编写将由他人使用的API代码,您的选择将是,而不是听Bob; - )

对于那些没有阅读过CleanCode的人,Bob建议做两件事:

1.您不应该编写返回null 的方法(以避免之后进行不必要的检查)。所以不要写这个:

var myObject = GetObjectThatDoesSomthing();  
if(myObject != null)  
{  
myObject.DoSomething();  
}

......你应该写下这个:

var myObject = GetObjectThatDoesSomething();  
myObject.DoSomething();  

清洁。

2.您不应该将null 传递给您的方法,以避免在方法开头进行不必要的检查,例如:

public Point Add(Point p1, Point p2)  
{  
    if(p1 == null) throw ArgumentException();  
    if(p2 == null) throw ArgumentException(); 
    ... 
}

这些规则的要点是:如果你坚持使用它,你知道你不必编写这些空检查,你的代码变得更清晰,更容易阅读。但是,在您使用第三方代码的那一刻,您无法判断他们是否在其API中应用了相同的规则,因此您将开始再次进行预检或后检查。当你为其他人编写API时,同样的事情:你的API的消费者如何知道你已经用Bobs规则编码了......

答案 2 :(得分:1)

这取决于您编写的代码类型。 如果您的公共方法旨在供不熟悉使用的各种开发人员使用,那么检查参数并抛出冗长的异常总是有意义的。

如果您正在编写一个私有方法,该方法仅在同一个类中使用,或者由您或您的协作者编写的另一个友好类调用的内部函数,则进行偏执无效检查则不太合理。您的注射设计和测试必须确保您的内部不会获得空值。

如果私有/内部方法参数仍然为空,那么它总是太迟了。从私有/内部方法抛出ArgumentNull异常无助于外部用户修复原因,因此对他来说无法获得ArgumentNull或NullReference异常。