最近,我正在阅读罗伯特·马丁(Robert Martin)的书“干净的代码”,在第7章中有一段,他说您应该尝试使用特殊情况设计模式替换空检查。我以为这是个好主意,但后来想到了以下情况。
foreach(thing in someCollection) {
value = getValue();
if(value == null) {
break;
}
value.doSomething();
}
您可以这样做,以使getValue
返回的对象是多态的,创建一个强制执行doSomething()
方法的接口,并为您要返回的对象实现该接口,并为该对象的“ mock”类特殊情况(如果值为null)。这将消除对gaurd子句的需要,因为如果在我们的“模拟”对象上调用doSomething()
并不重要,因为它是具有doSomething()
方法的实际对象:
function doSomething(){
return;
}
唯一的问题是,循环不会中断。据我所知,如果返回了“模拟”对象,则没有办法使用多态类打破循环,除非您对模拟对象进行了检查,但是那样就无法解决问题了。
我的问题是,有没有一种干净的方法来处理不会引起这种计算损失的空值?还是我对第7章关于空值检查的内容有误解?
答案 0 :(得分:2)
大概break
是为了防止NullPointerException
,因为已经到达集合的末尾。如果目标不是 filter 集合,则应该保证使用更合适的结构(例如Predicate
)(并且更具可读性)。
从高层次看,您已经观察到将小的OO构造放入一批过程代码中可能需要重新考虑以前的设计。这通常是正确的。如果将null
用作集合中间的断点,则在实现空对象模式之前,需要考虑更大的设计问题。
尽管对于空对象模式(在重构循环之后)这仍然是一个不错的方案,但该模式并不适用于所有使用null
的方案。不幸的是,这并不是那么简单。 Optional
是一种更受欢迎的解决方案,并且还有更多。
答案 1 :(得分:2)
特殊情况模式或空对象模式用于统一处理所有情况。这样可以避免客户端代码具有多个执行路径。
在您给出的示例中,特殊情况必须区别对待,因为遇到特定值时必须停止迭代(在这种情况下为null
)。您不能为问题X应用解决方案来解决问题Y,尤其是当两个问题完全相反时。