Java 空对象模式

Java 空对象模式,java,oop,nullpointerexception,null,null-object-pattern,Java,Oop,Nullpointerexception,Null,Null Object Pattern,似乎有越来越多的人说,您永远不应该返回null,而应该始终使用null对象模式。在使用集合/映射/数组或调用isAuthenticated()等布尔函数时,我可以看到NOP的有用性 我在这方面还没有找到完全令人信服的东西。请容忍我在这里试图组织我的思想 我的理解是,不是返回空对象,而是返回一个已“归零”的有效对象 例如,客户端将调用以获取对象: Car car = getCar(); 如果不使用NOP,则在调用任何方法之前,需要检查getCar()返回的对象是否为null: if (car !

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

我在这方面还没有找到完全令人信服的东西。请容忍我在这里试图组织我的思想

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

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

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

if (car != null){
    color = car.getColor();
    doScreenStuff(color);
   }
使用NOP,而不是
getCar()
返回null,它现在返回一个已经有效地“归零”的对象。因此,现在我们不再需要执行
if(car!=null)
操作,只需请求颜色即可。所以,我假设我们的“调零”对象在调用color时会返回“none”

这有什么帮助?向前移动并调用空对象上的方法似乎和检查null一样痛苦。现在,当需要显示信息时,我们需要检查颜色是否为“无”,高度是否为0,或者其他任何值。因此,本质上,不是在处理开始时检查car是否为null,而是在处理结束后检查car对象是真实的car还是替代品。也就是说,我们不想显示一堆空对象,所以我们需要一些方法来过滤掉所有的空对象

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

很明显,一定有什么东西我忽略了。
提前感谢。

空对象模式只有在空对象具有合理的功能值时才有意义。正如您所描述的那样,其目的不是延迟null,而是通过使用仍然有效的实际数据段来表示虚无或空,从而完全消除null的概念。例如,树结构中的洞的自然情况


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

空指针对象,至少在我的经验中是这样的,您将空检查限制在一个中心位置,以避免空指针异常

如果很多服务都在使用CarFactory,那么让CarFactory处理空结果,然后让每个服务处理它就容易多了。此外,它还确保以相同的方式处理每个空结果,无论是什么都不做还是某些指定的逻辑。缺点是,如果处理不当,可能会导致一些暂时性的错误(特别是在空指针异常高声尖叫的情况下)

我不再经常使用它了。除了使用空检查之外,还有其他选择,例如使用Java8选项。也有人支持和反对这一点,这绝不是空对象模式的替代品

String result = Optional.ofNullable(someInteger)
   .map(Integer::valueOf)
   .map(i -> i + 1)
   .map(Object::toString)
   .orElse("number not present");

System.out.println(result);

如果你不明白这一点,那么它可能不是一个好的范例。OO编程的全部思想是让事情变得更简单。不要陷入需要采用其他人精心设计的基于模式的系统的想法。通常,学习各种模式并有效地使用它们需要大量的工作,因此最好是融入其中,而不是强迫自己使用它

就这个特定的模式而言,它假定了某种编程风格,这可能不适合您。我自己永远不会使用它,因为我返回null作为合法值(丢失的数据),在每种情况下处理方式都不同,所以“集中处理”对我来说毫无意义。当我返回布尔值时,我使用原语


这里的底线是,如果你觉得某个模式不自然,就不要使用它。

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

  • “追加”、“求和”或类似的操作,需要关联
    a.op(b).op(c)
    a.op(b.op(c))
    相同
  • “空”、“零”或“空”值,用作操作的中性元素或标识元素
  • null对象模式的经典示例是返回空列表或数组,而不是null。列表是一个幺半群,使用append作为操作,空列表作为中性元素

    现在,您在
    汽车
    示例中面临的问题是
    汽车
    不是真正的幺半群;没有“空车”或“中性车”的概念,也没有一种合理的操作可以将两辆
    车组合成一辆

    因此,您得到的建议是使用类似Java8的东西。诀窍在于无论
    T
    是什么类型,
    可选的
    都是幺半群:

  • monoid的“combine”操作是“如果第一个值不是
    空的,则选择第一个值,否则选择第二个值”:
    
    • x | | empty=x
    • empty | | x=x
  • 中性元素是
    Optional.empty()
    ,因为
    Optional.empty(
    
    class Customer {
        IDiscount dis = new NormalDiscount();
        public double CalculateDiscount() {
            return dis.Calculate();
        }
        // Code removed for simplicity
    }
    
    class DefaultedCustomer : Customer {
        IDiscount dis = null;
        public double CalculateDiscount() {
            return dis.Calculate(); <---- This will crash in parent
        }
        // Code removed for simplicity
    }
    
    public class DefaultDiscount : IDiscount {
        public void Calculate() {
            return 0;
        }
    }