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,可以自动使用默认值。
答案 0 :(得分:3)
对我来说,Null Object模式就像一个安慰剂。真实对象和空对象可能具有完全不同的含义,但行为非常相似。就像安慰剂一样,null对象会诱使你相信没有错,但可能是非常错误的。
我认为早期失败并经常失败是一种很好的做法。最后,您需要在某处区分真实对象和空对象,此时它与检查null
指针没有区别。
这种方法优于工作默认实现的优点是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。可以使用扩展方法实现一些这样的功能,但这看起来有点蠢。