空对象设计模式与空对象检查

Posted

技术标签:

【中文标题】空对象设计模式与空对象检查【英文标题】:Null object design pattern Vs null object check 【发布时间】:2015-11-20 01:49:14 【问题描述】:

为什么空对象设计模式优于空对象检查。 如果我们查看空对象设计模式中的内存占用,我们会创建一个相同类型的新虚拟对象。这表明如果我们在搜索查询中有大尺寸的对象和大量可空对象,这种模式将创建那么多的空对象,这将占用比简单的检查更多的内存,而我的成本可以忽略不计的性能延迟。 /p>

Null Object design pattern

【问题讨论】:

在 Java8 中,您可以使用 Optional 来避免 NullPointerExceptions 而无需执行额外的检查。 oracle.com/technetwork/articles/java/… C# 和 c++ 等其他语言呢 @MuhammadNasir 你越有经验,你就越会意识到一个无法运行或充满错误的超快程序比一个运行良好的足够快的程序更糟糕.一切都与性能和内存占用无关。避免错误更重要。 null 是许多 bug 的根源,检查 null 经常被遗忘。 @MuhammadNasir 我是第二个 JB Nizet。它不像“A 总是比 B 好”——事情。你总是需要考虑上下文。当然,在内存容量非常有限的嵌入式环境中,您会寻找较小的内存占用空间。但在现代服务器上,这可能不是您主要关心的问题。 我个人更喜欢 null 而不是使用包装器对象来获取值。返回值的空值检查应该总是如此。无论如何,编译器都会优化任何额外的空值检查代码。您总是希望将 null(无论是 null 还是 Null)分隔到不同的程序流中。如果它为空,我从不让一个对象通过箍。经常检查。然后很容易分离程序流,提供自定义错误消息。使用 Null 对象的开销和更复杂的编程对我来说不值得。无论如何你都要检查一下。 【参考方案1】:

null 的全部问题在于,如果您尝试访问 null 值,应用程序将抛出 NullPointerException 并中止。

要减少此空对象设计模式NullXXX类的数量(它实际上只是工厂设计模式,而不是模式本身),您可以创建一个始终返回的static final NullCustomer .

在 Java 8 中,您可以使用 Optional 方法来判断函数何时不总是返回值。这种方法不会强制您创建污染整体结构的任意空类(考虑也可能必须重构这些空类)。

Eclipse 和 IntelliJ 还提供编译时注释 @Nullable@NonNull,在访问潜在的 null 对象时会发出编译器警告。但是,许多框架没有注释。 IntelliJ 因此尝试通过静态分析来discover those potential null accesses。 除了很少采用这种方法外,IntelliJ 和 Eclipse 使用它们自己的注释(org.eclipse.jdt.annotation.NonNullcom.intellij.annotations.NotNull),这些注释不兼容。但是,您可以在 IntelliJ 中使用 store the annotations outside of the code。 Eclipse 也希望在未来实现这一点。问题是many frameworks providing this feature 给你许多不同的注释做同样的事情。有JSR-305 处于休眠状态。它会在javax 中提供注释。我不知道他们为什么没有进一步推动这一点。

【讨论】:

只是为了修改这个“Eclipse 也希望在未来实现这个”:已通过 Eclipse Mars (4.5) 发布了对外部注释的支持。 另一个澄清:“IntelliJ 和 Eclipse 使用它们自己的注释 (org.eclipse.jdt.annotation.NonNull, com.intellij.annotations.NotNull),它们不兼容”。这些注释中只有一件事是不兼容的:@Target。 Eclipse 提供了两个版本,org.eclipse.jdt.annotation_1.x 用于 1.7 及更低版本(“声明注释”)和 org.eclipse.jdt.annotation_2.x 用于 1.8 及更高版本(“类型注释”,其中目标是 @ 987654340@ 进行更强大的检查)。除此之外,Eclipse 可以配置为使用任何一组正确定义的空注释。 只是添加到答案中,使用 Optional 通常是正确的解决方案,但是当您使用返回的对象回答一堆不同的问题时,每个答案都依赖于返回对象的值字段,当委托给 Null 对象时,逻辑变得更容易维护。那是在那些1/20的情况下;其他 19/20 通常使用 Optional 会更好。【参考方案2】:

使用空对象而不是null 的主要优点是使用null 您必须重复检查该对象是否确实是null,尤其是在所有需要该对象的方法中目的。

在 Java 8 中,必须这样做:

Object o = Objects.requireNotNull(o); //Throws NullPointerException if o is indeed null.

所以,如果你有一个方法不断地将同一个对象传递给不同的方法,那么每个方法在使用之前都需要检查接收到的对象不为空。

因此,更好的方法是使用 Null 对象或 Optional(Java 8 及更高版本),这样您就不需要一直进行 null 检查。取而代之的是:

Object o = optional.get(); //Throws NullPointerException if internal value is indeed null.
//Keep using o.

没有(真的)需要空值检查。你有一个Optional 的事实意味着你可能有一个值,也可能没有。

Null 对象没有副作用,因为它通常什么都不做(通常所有方法都是空方法),因此无需担心性能(瓶颈/优化/等)。

【讨论】:

您仍然需要检查 Optional 是否存在,并且 NullPointerException 仍然是您想要避免的事情。只是,因为它是一个 Optional,所以现在很明显它可能不存在。 @JB Nizet 确实如此。我试图强调没有每次都做null检查。 使用空注释,您可以将所有这些方法指定为采用@NonNull 参数。然后调用者将对有问题的局部变量执行一次空检查/断言,并将这个变量传递给所有方法调用,一切都很好。另外:这避免了对预期执行某些工作的方法调用的潜在混淆,但实际上它们可能是无操作的。【参考方案3】:

这种模式的主要区别(也可能是优势)是独特性。想想下面的方法定义:

public static int length(String str);

此方法计算给定字符串的长度。但是参数可以是null吗?该方法将做什么?抛出异常?返回 0?返回-1?我们不知道。

通过编写好的 java doc 可以实现一些部分解决方案。下一个更好的解决方案是使用注释 JSR305 注释 @Nullable@NotNullable,但是开发人员可以忽略它们。

但是,如果您使用的是 Null 对象模式(例如,guava 或 java 8 的可选),您的代码如下所示:

public static int length(Optional<String> str);

所以开发人员必须关心将他的字符串包装到 Optional 中,因此理解参数可以为空。尝试从 Optional 中获取包含 null 的值会导致使用常规 null 时并不总是发生异常。

显然你是对的,使用这种模式会导致一些额外的 CPU 和内存消耗,但在大多数情况下并不显着。

【讨论】:

Null Object Pattern (en.wikipedia.org/wiki/Null_Object_pattern) 与 Optional 不同。【参考方案4】:

假设你有这样的东西:

private SomeClass someField;

void someMethod() 
  // some other code
  someField.method1();
  // some other code
  someField.method2();
  // some other code
  someField.method3();

现在假设当someField 可以是null 时存在有效用例,并且您不想调用它的方法,但您想执行该方法的其他some other code 部分。您需要将方法实现为:

void someMethod() 
  // do something
  if (someField != null) 
    someField.method1();
  
  // do something
  if (someField != null) 
    someField.method2();
  
  // do something
  if (someField != null) 
    someField.method3();
  

通过将 Null 对象与空(无操作)方法一起使用,我们避免了样板的 null 检查(以及忘记为所有事件添加检查的可能性)。

我经常发现这在异步或可选初始化某些内容的情况下很有用。

【讨论】:

以上是关于空对象设计模式与空对象检查的主要内容,如果未能解决你的问题,请参考以下文章

设计模式空对象模式

设计模式(32)-----行为型模式-----空对象设计模式

空对象模式

空对象模式

设计模式系列 - 空对象模式

案例分析:设计模式与代码的结构特性