似乎有越来越多的人说你永远不应该返回null,而应该总是使用Null对象模式。我可以在使用集合/ map /数组或调用boA函数(如isAuthenticated(),which is shown here时看到NOP的有用性。
我没有发现任何完全令人信服的内容。当我试图组织我的想法时,请在这里忍受。
我的理解是,不是返回一个空对象,而是返回一个已经“归零”的有效对象。
因此,例如,客户端将调用以获取对象:
Car car = getCar();
如果没有使用NOP,你需要检查从getCar()返回的对象是否为null,然后再调用它上面的任何方法:
if (car != null){
color = car.getColor();
doScreenStuff(color);
}
使用NOP而不是getCar()
返回null,它现在返回一个已被有效“清零”的Object。所以现在我们不再需要if (car != null)
并且可以只请求颜色。所以,我认为当我们调用颜色时,我们的“归零”对象将返回“无”。
这有什么用?似乎向前移动并在空对象上调用方法会导致与仅检查null相同的痛苦。现在,当需要显示信息时,我们需要检查颜色是否为“无”,高度不为0,或者您拥有的其他任何值。因此,基本上,如果汽车为空,则不检查处理的开始,而是检查我们拥有的汽车对象是真车还是替代品。 I.E.我们不想显示一堆空对象,所以我们需要一些方法来过滤掉所有空对象。
这个过滤是一个额外的步骤,就像调用if(car!= null)一样。唯一的区别是,通过检查null,我们可以在通过抛出异常发现car对象为null时立即停止处理,而使用NOP我们在空对象上调用方法并继续前进,直到它到达时间为止显示对象,此时我们过滤掉空心。此外,您需要知道空对象返回的值。 I.E. getColor()返回“none”或“empty”。
显然必须有一些我忽略的东西。 提前谢谢。
答案 0 :(得分:12)
MattPutnam的答案是正确的,我是第二个。我要补充一点:当你分析它时,“空对象”的概念似乎归结为monoid的数学概念。你可以这样想:monoid是一种具有这两种东西的类型:
a.op(b).op(c)
与a.op(b.op(c))
相同。空对象模式的经典示例是返回空列表或数组而不是null。好吧,列表是一个幺半群,附加作为操作,空列表作为中性元素。
现在,您在Car
示例中遇到的问题是Car
并非真正的幺半群;没有“空车”或“中性车”的概念,并且没有一个合理的操作可以用来将两个Car
组合成一个。
因此,您正确得到的建议是使用类似Java 8 Optional
的内容。诀窍在于,无论T
是什么类型,Optional<T>
都是幺半群:
empty
则选择第一个值,否则选择第二个值”:
x || empty = x
empty || x = x
Optional.empty()
,因为Optional.empty().orElse(anything)
与anything
相同。基本上,Optional<T>
是一个包装器,它将空对象添加到没有一个类似Car
的类型。 Optional<T>.orElse(T value)
方法是“选择第一个非empty
值”monoid的略微重构版本。
答案 1 :(得分:8)
只有当空对象具有合理的功能值时,空对象模式才有意义。正如您所描述的那样,目的不是推迟null,而是通过用仍然有效的实际数据表示虚无或空虚来完全消除null的想法。例如,树结构中空洞的自然情况as described in the Wikipedia article。
空车没有意义。在这种情况下,getCar()
返回Optional<Car>
似乎更合适。
答案 2 :(得分:2)
如果你没有看到这一点,那么它可能不是一个适合你的好范例。面向对象编程的整个想法是让事情变得更简单。不要陷入困境,认为你需要采用别人精心设计的基于模式的系统。通常需要花费大量的工作来学习各种模式并有效地使用它们,所以最好成长为它们,而不是试图强迫自己使用它。
就这个特定的模式而言,它假设某种程式的编程可能不适合你。我永远不会自己使用它,因为我将空值作为合法值(缺失数据)返回,在每种情况下处理不同,因此“集中处理”对我来说没有意义。当我返回布尔值时,我使用原语。
这里的底线是,如果图案看起来不自然,请不要使用它。
答案 3 :(得分:0)
至少根据我的经验,Null指针对象是这样的,你可以将空检查限制在一个中心位置,以避免空指针异常。
如果很多服务都在使用CarFactory,那么让Carfactory处理null结果要容易得多,然后让每个服务处理它。此外,它确保每个null结果以相同的方式处理,无论是无效还是某些指定的逻辑。缺点是,如果处理不当,可能会导致一些暂时混乱的错误(特别是因为空指针异常会大声尖叫并引以为傲)。
我真的不再使用它了。还有使用空检查的替代方法,例如使用Java 8 Optional。也有人支持和反对这一点,这绝不是零对象模式的替代品。
String result = Optional.ofNullable(someInteger)
.map(Integer::valueOf)
.map(i -> i + 1)
.map(Object::toString)
.orElse("number not present");
System.out.println(result);
答案 4 :(得分:-1)
NULL设计模式使用DEFAULT行为填充对象的ABSENCE,并且只应在一个对象与其他对象协作时使用。
NULL设计模式并不意味着替换NULL异常处理。这是NULL设计模式的附带好处之一,但目的是提供默认行为。
例如,请考虑以下示例pseduo代码。它是一个简单的Customer类,它将折扣计算委托给折扣对象。
class Customer {
IDiscount dis = new NormalDiscount();
public double CalculateDiscount() {
return dis.Calculate();
}
// Code removed for simplicity
}
现在让我们说我们想要创建违约付款的客户。所以我们继承了上面的类,因为很多属性和行为是相同的,但是我们不想要折扣计算,因为默认的客户没有资格享受折扣。因此我们将折扣对象设为null,但这是一个问题,因为现在折扣计算会因为NULL折扣对象而崩溃。
class DefaultedCustomer : Customer {
IDiscount dis = null;
public double CalculateDiscount() {
return dis.Calculate(); <---- This will crash in parent
}
// Code removed for simplicity
}
因此,开发人员解决此问题的方法之一是检查NULL并返回零。如果你睁大眼睛并从逻辑上思考这实际上是商业场景,对于违约客户没有折扣。
因此,通过检查NULL,我们可以创建一个DEFAULT DISCOUNT BEHAVIOR类,而不是通过检查NULL来修复此问题,如下所示,它返回零折扣并对默认客户使用相同的折扣。这比检查NULL更干净。
public class DefaultDiscount : IDiscount {
public void Calculate() {
return 0;
}
}
需要NULL,但异常处理不是为了在协作中修复对象的缺失,如上所示。如果没有协作,则NULL设计模式没有意义。
由于NULL设计模式,可以避免NULL检查,但更多 后效和副作用但不是主要意图。
我从这个NULL Design pattern in C#中解释了以上所有的想法,它解释了Do和Donts of Don。
答案 5 :(得分:-3)
返回可选汽车不会带走检查汽车对象是否确实存在的步骤。
当回车时,你会检查是否(car!= null)。 同样,当访问Optional时,你应该检查是否(car.isPresent())然后检查car.get()。
执行get()而不检查是否存在是不可接受的,并且可以使用checkstyle配置轻松识别和避免错误。
你可能会问这个问题。如果我们检查它是否存在于两种模式中,我们会得到什么好处?
答案在于知道在使用之前必须进行检查。如果你严格遵循返回Optional的做法,只要它可以为空,那么你不需要检查空值,只要它不是可选的。
它作为一种记录方法的程序化方式,在内部帮助通过检查方式执行检查。