空对象模式

似乎有越来越多的人说,你永远不应该返回null,而应该始终使用Null对象模式。我可以看到NOP在使用集合/映射/数组或调用布尔函数(如isAuthenticated())时的有用性,如下所示

我没有发现任何完全令人信服的东西。在这里忍受我,因为我试图组织我的想法。

我的理解是,您不是返回空对象,而是返回一个已被“清零”的有效对象。

例如,客户端将调用以获取对象:

Car car = getCar();

如果不使用 NOP,则需要在调用其上的任何方法之前检查从 getCar() 返回的对象是否为 null:

if (car != null){
    color = car.getColor();
    doScreenStuff(color);
   }

使用 NOP,它现在不返回 null,而是返回一个已有效“清零”的对象。所以现在我们不再需要做,可以只要求颜色。所以,我想当我们调用颜色时,我们的“归零”对象将返回“无”。getCar()if (car != null)

这有什么帮助?似乎继续前进并在空对象上调用方法会导致与仅检查null一样痛苦。现在,当需要显示信息时,我们需要检查颜色不是“无”,高度不是0,或者你拥有的任何其他值。因此,从本质上讲,如果汽车为空,则无需在处理开始时进行检查,而是在之后检查我们拥有的汽车对象是真正的汽车还是替代品。也就是说,我们不想显示一堆空对象,因此我们需要某种方法来过滤掉所有空对象。

此筛选是一个附加步骤,就像调用 if (car != null) 一样。唯一的区别是,通过检查null,我们可以在发现car对象为null时立即通过抛出异常来停止处理,而使用NOP,我们在空对象上调用方法并继续摇摆,直到是时候显示对象,此时我们过滤掉空。此外,您需要知道空对象返回的值。即 getColor() 返回 “none” 或 “empty”。

显然,我一定忽略了一些东西。提前致谢。


答案 1

MattPutnam的答案是正确的,我支持它。我想补充一点:当你分析“空对象”的概念时,似乎可以归结为幺半群的数学概念。你可以这样想:幺半群是一种同时具有这两种东西的类型:

  1. 需要关联 :的“追加”、“求和”或类似操作与 相同。a.op(b).op(c)a.op(b.op(c))
  2. 一个“空”、“零”或“null”值,用作操作的中性元素标识元素

null 对象模式的经典示例是返回空列表或数组,而不是 null。好吧,列表是一个幺半群,附加作为操作,空列表作为中性元素。

现在,您在示例中遇到的问题是,这并不是真正的幺半群。没有“空车”或“中性车”的概念,也没有一个明智的操作可以用来将两个s合二为一。CarCarCar

因此,您得到的建议是正确的是使用类似Java 8 Optional的东西。诀窍是,无论是什么类型,都是一个幺半群:TOptional<T>

  1. 幺半群的“组合”运算是“如果不是,则选择第一个值,否则选择第二个值”:empty
    • x || empty = x
    • empty || x = x
  2. 中性元素是 ,因为 与 just 相同。Optional.empty()Optional.empty().orElse(anything)anything

所以基本上,是一个包装器,它将一个空对象添加到没有空对象的类型中。该方法是“选取第一个非值”幺半群的略微重构版本。Optional<T>CarOptional<T>.orElse(T value)empty


答案 2

null 对象模式仅在 null 对象具有合理的功能值时才有意义。目的不是像你所描述的那样推迟空,而是通过用仍然有效的实际数据来表示虚无或空虚来完全消除空的概念。例如,树结构中孔的自然情况,如维基百科文章中所述

空车没有意义。在这种情况下,似乎更合适的事情是 返回 。getCar()Optional<Car>