Mis*_*ith 12 c# java null exception
我在项目中遇到了这个问题:有一系列嵌套对象,例如:类A包含一个B类的实例变量,它实际上有一个类C的实例变量,......,直到我们有一个节点Z级树.
----- ----- ----- ----- -----
| A | ---> | B | ---> | C | ---> | D | ---> ... ---> | Z |
----- ----- ----- ----- -----
Run Code Online (Sandbox Code Playgroud)
每个类为其成员提供getter和setter.父A实例由XML解析器创建,链中的任何对象都为空是合法的.
现在假设在应用程序的某个点上,我们引用了一个A实例,并且只有它包含一个Z对象时,我们必须在它上面调用一个方法.使用定期检查,我们得到以下代码:
A parentObject;
if(parentObject.getB() != null &&
parentObject.getB().getC() != null &&
parentObject.getB().getC().getD() != null &&
parentObject.getB().getC().getD().getE() != null &&
...
parentObject.getB().getC().getD().getE().get...getZ() != null){
parentObject.getB().getC().getD().getE().get...getZ().doSomething();
}
Run Code Online (Sandbox Code Playgroud)
我知道异常不应该用于普通的控制流,但是我看到一些程序员这样做,而不是以前的代码:
try {
parentObject.getB().getC().getD().getE().get...getZ().doSomething();
} catch (NullPointerException e){}
Run Code Online (Sandbox Code Playgroud)
这段代码的问题在于它在维护时可能会混淆,因为它没有清楚地显示允许哪些对象为null.但另一方面更简洁,更不"伸缩".
这样做可以节省开发时间吗?如何重新设计API以避免此问题?
我唯一能想到的就是避免isValid长空检查是为了提供嵌套对象的void实例并为它们中的每一个提供方法,但这不会在内存中创建很多不必要的对象吗?
(我使用过Java代码,但同样的问题可以应用于C#属性)
谢谢.
joe*_*rgl 16
如果parentObject需要知道A包含一个包含C的B,那么这是一个糟糕的设计....这样,一切都与所有东西相连.你应该看一下demeter法则:http://en.wikipedia.org/wiki/Law_Of_Demeter
parentObject应该只调用其实例变量B上的方法.因此,B应该提供一个允许决策的方法,例如
public class A {
private B myB;
//...
public boolean isItValidToDoSomething(){
if(myB!=null){
return myB.isItValidToDoSomething();
}else{
return false;
}
}
}
Run Code Online (Sandbox Code Playgroud)
最终,在Z级别,该方法必须返回true.
Imho,节省开发时间绝不是容忍设计问题的理由.这些问题迟早会比你先解决问题所花费的时间更多
就我个人而言,我喜欢通过使用选项类型来完全避免这个问题。通过调整从这些方法/属性返回的值,而Option<T>不是T调用者可以选择他们希望如何处理没有值的情况。
选项类型可以有包含值,也可以没有(但选项本身永远不能为空),但调用者不能简单地传递它而不解开值,因此它迫使调用者处理可能没有值的事实。
例如在 C# 中:
class A {
Option<B> B { get { return this.optB; } }
}
class B {
Option<C> C { get { return this.optC; } }
}
// and so on
Run Code Online (Sandbox Code Playgroud)
如果调用者想要抛出,他们只是检索值而不显式检查是否有:
A a = GetOne();
D d = a.Value.B.Value.C.Value.D.Value; // Value() will throw if there is no value
Run Code Online (Sandbox Code Playgroud)
如果调用者想要在任何步骤没有值的情况下默认,他们可以执行映射/绑定/投影:
A a = GetOne();
D d = a.Convert(a => a.B) // gives the value or empty Option<B>
.Convert(b => b.C) // gives value or empty Option<C>
.Convert(c => c.D) // gives value or empty Option<D>
.ValueOrDefault(new D("No value")); // get a default if anything was empty
Run Code Online (Sandbox Code Playgroud)
如果调用者希望在每个阶段都默认,他们可以:
A a = GetOne();
D d = a.ValueOrDefault(defaultA)
.B.ValueOrDefault(defaultB)
.C.ValueOrDefault(defaultC)
.D.ValueOrDefault(defaultD);
Run Code Online (Sandbox Code Playgroud)
Option目前还不是 C# 的一部分,但我想有一天会成为的。您可以通过引用 F# 库来获取实现,也可以在 Web 上找到实现。如果您想要我的,请告诉我,我会将其发送给您。
| 归档时间: |
|
| 查看次数: |
630 次 |
| 最近记录: |