为每个类创建一个NullObject是否可行? (当然有工具)

时间:2010-07-16 15:00:39

标签: language-agnostic design-patterns nullpointerexception oop null-object-pattern

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,可以自动使用默认值。

6 个答案:

答案 0 :(得分:3)

对我来说,Null Object模式就像一个安慰剂。真实对象和空对象可能具有完全不同的含义,但行为非常相似。就像安慰剂一样,null对象会诱使你相信没有错,但可能是非常错误的。

我认为早期失败并经常失败是一种很好的做法。最后,您需要在某处区分真实对象和空对象,此时它与检查null指针没有区别。

来自Wikipedia article

  

这种方法优于工作默认实现的优点是Null对象非常可预测且没有副作用:它 nothing

它也不会指出任何问题。想想当null对象在应用程序中一直移动时会发生什么。然后,在某些时候,您的应用程序需要对象的某些行为,而null对象实现无法提供。此时,您的应用程序可能会崩溃或进入无效状态。您将有一个非常困难的时间跟踪空对象的原点null指针会在开头抛出异常,引起您的注意直接关注问题的根源。

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

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

答案 1 :(得分:2)

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

另外,返回新对象的第三方库怎么样?你会在这些之前加上某种“接口”,这样如果它们返回null,你会用一种适当的nullObject替换吗?

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

在这种情况下,ImageNullObject应该返回...“IMAGETYPE.GIF”? “IMAGETYPE.UNKNOWN”?空?

答案 2 :(得分:2)

我认为你只是想把错误条件推到一层。

当一个应用程序请求一个对象,并且它为null时,那么你需要处理这个条件,而不仅仅是希望它是正常的。如果对象为null,并且您尝试使用它,则会出现错误,那么为什么要强制对其余代码进行所有错误检查呢?

答案 3 :(得分:0)

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

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

即使使用返回void的方法,并不总是返回null功能的情况。例如,某些类可能需要在父/子委托对象完成自己的事物(在本例中为 是无操作)时调用该方法,以便保留班级的不变量。

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

答案 4 :(得分:0)

除了测试之外,我通常称这种类型的概念编码为Slop。这是快速失败的反面,你推迟找到你没有编码的州的任何机会。

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

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

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

除了紧急补丁和测试之外我真的看不出这种编码的任何借口,除了“我不理解代码,我觉得这样做不对,但我不想尝试理解它“ - 不是,IMO,是工程师的有效借口。

答案 5 :(得分:0)

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

Class Bar
  Dim Value as Integer
  Function Boz() As Integer
    Return Value
  End Sub
  Sub Nothing.Bar() As Integer
    Return -9
  End Sub
End Class

如果Foo为非null,则尝试评估Foo.Bar()将产生Foo.Value,否则为-9。可以使用扩展方法实现一些这样的功能,但这看起来有点蠢。