空对象模式中的空值检查

4

空对象模式的主要目标是确保向客户端提供可用的对象。因此,我们希望替换以下代码...

void Class::SetPrivateMemberA() {
    m_A = GetObject();
}

void Class::UseA() {
    if (m_A != null) {
        m_A.Method();
    } else {
        // assert or log the error
    }
}

使用这种实现方式:

void Class::SetPrivateMemberA() {
    m_A = GetObject();
}

void Class::UseA() {
    m_A.Method();
}

我思考的问题是,GetObject()仍然返回一个对象,一个空对象或其他对象。我喜欢不重复检查null并相信返回的对象可用的想法,但为什么我不在第一次实现中就这样做呢?

空对象模式的优势只是稍微增加了对清理代码的信任吗?在第二个实现中,调用A.Method()之前不检查它是否为null仍然是一个好习惯吗?

2个回答

5

您是正确的,如果您确定您从不返回null,则在第一种实现中调用方法之前可以跳过null检查。同样,如果需要在null对象上执行特殊操作,例如UseA()需要在null对象上执行不同的操作,那么您需要显式检查null对象。但是,null对象模式真正有帮助的是那些并不重要的情况。

例如,大多数观察者模式。如果您将观察者模式实现为类的成员,并且只能有一个观察者,请通知观察者您的类已经完成某些操作,那么对于类而言观察者是否为空并不重要。

这也适用于空容器类,它们本质上是null对象模式:在查询中不返回空容器,而是返回一个空容器。这样做可以避免像迭代所有容器条目这样的问题,往往无论其是否为空都不重要,因此消除了对null检查的需求,使代码更易于维护/更易于阅读。然而,如果您想填充数据集的视图,则仍然需要显式显示不同的“无条目”以检查是否为空容器。

编辑以获得更清晰的表述

一个问题是只从调用方考虑问题。像大多数设计模式一样,这需要同时考虑两个方面才能得到充分利用。请考虑:

public PossiblyNull GetSomethingNull()
{
    if (someBadSituation())
        return null;
    else
        return SomehowProduceSomething();
}

vs

public PossiblyEmpty GetSomethingEmpty()
{
    if (someBadSituation())
        return StaticEmptySomething();
    else
        return ProdueSomethingYay();
}

现在,您的调用代码不再像这样:
public void DoSomethingWithChild(Foo foo)
{
    if (foo != null)
    {
        PossiblyNull bar = foo.GetSomething();
        if (bar != null)
            bar.DoSomething();
    }
}

它可以

public void DoSomethingWithChild(Foo foo)
{
    if (foo != null)
        foo.GetSomething().DoSomething();
}

我同意你和卡尔的评论。我认为这种做法过于谨慎,会使代码变得复杂,减缓代码审查等。问题在于如何应对“如果”论点:“如果在生产环境中发生了某些事情,我们没有检查空值而导致崩溃,怎么办?”你会如何回应? - TERACytE
@TERACyTE:嗯,当不需要时添加用于空状态的代码分支也会增加复杂性,难以维护。 不检查 null 的理念与空对象模式是相当独立的,后者是关于将 null 用作设计上的常规发生和将其视为异常情况的一种处理方法。 - Tanzelax
这里的想法是,如果事物仍然可以为空,您仍然需要检查空值。但是,通过考虑那些可能导致空值的来源,您可以使代码分支更易于维护。 - Tanzelax
感谢您的反馈。谢谢。 - TERACytE

0
使用第二种实现方式,调用 A.Method() 前检查它是否为 null 是否仍然是一种好的编程习惯呢?
不是。如果你知道 m_A 不为 null,那么这个检查就是多余的;这是过度谨慎编码的例子。它会带来什么危害?它使你的代码变得复杂 - 没有必要的复杂性;它使代码更难读、更难调试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接