4

NullObjectPattern旨在成为一种“安全”(中性)行为。

这个想法是创建一个不做任何事情的对象(但也不抛出 NullPointerException )

例如定义为的类:

class Employee {
    private String name;
    private int age;
    public String getName(){ return name; }
    public int getAge()    { return age;  }
}  

将在此代码中导致 NullPointerException:

 class Other {
      Employee oscar;

      String theName = oscar.getName(); // NPE

 }

NOP 说的是,您可以拥有这样的对象:

 class NullEmployee extends Employee {
      public static final  Employee instance = new NullEmployee(); 
      public String getName(){ return ""; }
      public int getAge()    { return 0;  }
 }

然后将其用作默认值。

 Employee oscar = NullEmployee.instance;

问题来了,当您需要为您创建的每个类重复代码时,解决方案就是拥有一个创建它的工具。

创建这样的工具或使用它(如果存在)是否可行/合理/有用?

也许使用 AOP 或 DI 可以自动使用默认值。

4

6 回答 6

3

对我来说,空对象模式感觉就像是安慰剂。真实对象和空对象可能具有完全不同的含义,但行为非常相似。就像安慰剂一样,null 对象会诱使您相信没有任何问题,但某些事情可能非常错误。

我认为尽早失败并经常失败是一个好习惯。最后,您需要在某处区分真实对象和空对象,此时它与检查null指针没有什么不同。

来自维基百科的文章

这种方法相对于工作默认实现的优点是 Null 对象是非常可预测的并且没有副作用:它什么都不做。

它也不会指出任何问题。想一想当一个空对象一路穿​​过您的应用程序时会发生什么。然后,在某些时候,您的应用程序期望对象的某些行为,而空对象实现无法提供这些行为。那时您的应用程序可能会崩溃或进入无效状态。您将很难追踪空对象的来源。指针会在一null开始就引发异常,从而将您的注意力直接吸引到问题的根源上。

维基百科文章给出的唯一例子是空集合而不是null. 这是一个非常好的做法,但也是空对象模式的一个糟糕示例,因为它处理的是对象集合,而不是单个实例。

简而言之,我确信为所有类创建空对象实现是可行的,但我强烈建议不要这样做。

于 2010-07-19T18:55:32.043 回答
2

我不确定这是个好主意。在某些情况下,“nullObject”可能很有用并且非常有意义,但是对每个类都使用它是一种矫枉过正。特别是因为这可能会使一些错误(或分析中的空白)非常难以诊断。

另外,返回新对象的第三部分库呢?您是否会在这些前面放置某种“接口”,以便在它们返回 null 时您将替换为 nullObject 的适当风格?

您提到您正在尝试将其自动化 - 在某些情况下是否需要实际的设计决策才能返回适当的值?即,假设您有一个 Image 对象,它返回一个“ImageType”(.gif、.jpg 等的枚举)

在这种情况下,ImageNullObject 应该返回...“IMAGETYPE.GIF”?“图像类型。未知”?无效的?

于 2010-07-16T15:07:31.283 回答
2

我认为您只是在乞求将错误条件向下推一层。

当应用程序请求一个对象时,它是空的,那么你需要处理那个条件,而不仅仅是希望它是好的。如果对象为空,而您尝试使用它,您将得到错误,那么为什么要强制对其余代码进行所有错误检查呢?

于 2010-07-16T15:10:08.183 回答
0

我希望看到的是编译器允许使用静态定义的类型定义空对象的行为。例如,如果“Foo”属于“bar”类:

班级酒吧
  暗值作为整数
  函数 Boz() 作为整数
    返回值
  结束子
  Sub Nothing.Bar() As Integer
    返回 -9
  结束子
结束类

如果 Foo 不为空,则尝试评估 Foo.Bar() 将产生 Foo.Value,否则为 -9。可以使用扩展方法实现一些这样的功能,但这似乎有点恶心。

于 2010-12-14T04:31:15.787 回答
0

我认为自动生成功能性空对象会很棘手。(您当然可以创建 shell,然后去填写实现本身)。

举个简单的例子——返回 的方法的 null 功能是int什么?它应该返回 -1、0 还是其他一些值?返回字符串的东西怎么样 - 再次,是null还是""正确的选择?对于返回其他类的方法 - 如果它是您自己的类之一,那么您可能可以为该类使用 Null 对象,但您将如何访问它?也许你可以让你编写的每个类都实现一些带有空对象访问器的接口,但这值得吗?

即使使用 returnvoid的方法,null 功能也并非总是简单地返回。例如,某些类可能需要在完成自己的事情时调用父/委托对象上的方法(在这种情况下是无操作),以保留类的不变量。

所以基本上 - 即使要实现“空”行为,你也必须能够推断出这种行为是什么,我希望这对于工具本身来说太先进了。也许你可以用类似的东西来注释你的所有方法@NullReturns(-1),并有一个工具来选择它们并形成空对象实现,但即使这样也不能涵盖所有情况,你也可以直接编写对象而不是编写它的所有功能注释。

于 2010-07-16T15:09:47.653 回答
0

除了测试,我一般把这种类型的概念编码称为 Slop。这与快速失败相反,您推迟了找到您没有编码的状态的任何机会。

这里的要点是为什么你的对象可能返回一个不在方法契约中的值?当你设计和编码方法的契约时,你要么说它可以返回 null,要么不能返回,对吧?因此,如果您可以返回 null,这很重要。如果你不能返回 null,这很重要。

此外,如果您使用的对象引用可以为 null,那么这是一个重要的值,您不应该深入研究可能访问该对象方法的代码。

最重要的是,尽可能使用最终/常量/不变变量,并且(至少对于 OO 语言,将数据隔离在可以确保正确状态的构造函数后面)。如果不可变对象正确设置了它的变量,那么这些变量就不可能返回无效值。

除了紧急补丁和测试,我真的看不出这种编码的任何借口,除了“我不理解代码,我认为这不能正常工作,但我不想尝试理解它” - - 这不是,IMO,工程师的正当借口。

于 2010-07-22T21:58:55.130 回答