C# 试图重构为空对象模式,但最终结果似乎更糟

C# 试图重构为空对象模式,但最终结果似乎更糟,c#,refactoring,null-object-pattern,C#,Refactoring,Null Object Pattern,我正在使用null对象模式重构一个大类,该类在所有地方都有大量的null检查。到目前为止,这几乎是一个平稳的变化,但我对最终结果有几个问题,我想知道是否有更好或不同的方法,甚至是回到过去的方式 最大的问题是我有以下代码: IMyObject myObject = GetMyObject(); if(myObject != null && !myObject.BooleanProperty) DoSomething(); 正如您所看到的,我可能会从这个条件中删除空检查,但是

我正在使用null对象模式重构一个大类,该类在所有地方都有大量的null检查。到目前为止,这几乎是一个平稳的变化,但我对最终结果有几个问题,我想知道是否有更好或不同的方法,甚至是回到过去的方式

最大的问题是我有以下代码:

IMyObject myObject = GetMyObject();
if(myObject != null && !myObject.BooleanProperty)
   DoSomething();
正如您所看到的,我可能会从这个条件中删除空检查,但是我有一个布尔属性,如果设置为默认值,它将执行一段代码。如果我总是返回true,我可能会引入一些微妙的bug,这些bug很难找到并消除

另一个问题是,我不得不修改null的检查,如下所示:

if(myObject.GetType() != typeof(MyNullObject))
   return false;

DoSomething();
这很难看,因为我现在必须检查类型,而不是检查null。这种情况在类中发生了三次,因为我没有返回对象的一个属性或执行它的一个方法,所以我必须执行此检查

最后,对象有两个DateTime属性,它们不可为null,架构师不希望它们为null。同样,通过将MinDate值作为默认值,一些讨厌的bug可能会爬进代码中

好了。在这种情况下,空对象模式是否比散落在各处的意大利面空检查更糟糕?有没有更好的方法来实现这一点

谢谢你的回答

你应该看看。这是一种优雅的方法,可以在方法上强制执行前置和后置条件,以避免您所指的各种问题。当契约在运行时被破坏时,或者在某些情况下通过静态分析(Microsoft framework),它通过提前引发异常来实现这一点

我更喜欢,它不支持静态分析,但它有一个很好的流畅的界面,比微软的同类产品更直观

它允许您编写如下代码,甚至可以扩展接口:

public class MyClass
{
   public void MyMethod(string param1, int param2)
   {
       Condition.Requires(param1).IsNotNullOrWhiteSpace();
       Condition.Requires(param2).IsGreaterThan(0);

       ...
   }
}
您将对输入系统的所有数据实现条件,即所有公共方法,并防止开发人员编写违反此条件的代码。当它由于错误而发生时,异常和堆栈跟踪会准确地告诉您问题所在


还有一些方法可以设置监视属性的条件,以确保某些条件不会通过不变量出现。这可以作为一个反腐败层,再次捕获bug,并阻止开发人员编写破坏系统的代码。

您可以在接口IMyObject中添加一个布尔IsNull(IsEmpty)属性,然后实现MyNullObject,该属性返回true。显然,您必须相信,在其他情况下,这应该返回false,否则您将有错误的行为

重构您的代码,使
DoSomething()
成为空对象上的方法,并简单地实现为无操作,这样会更好吗?Null对象的另一个替代方法是。它使空检查更具可读性,调用代码更安全。

空对象模式是一种折衷——消除空检查会带来好处,但需要另一个类来维护

我建议在接口/基类中添加一个
IsNull
boolean属性

正常实现返回
false
;空对象返回
true


这允许您避免针对确切类型的测试,并且更清楚地了解您的意图。您还可以在处理日期的代码中将其用作测试,从而保留不可为null的日期属性。

如何定义一个静态方法或扩展方法来测试对象是否为null,如果为非null,则返回属性?如果在对象为null时它应该返回false,那么可以将其命名为PropertyNameOrFalse;如果在这种情况下它应该返回True,那么可以将其命名为PropertyNameOrTrue。还可以使用PropertyNameOrDefault(defaultValue),如果对象存在,则返回属性值;如果对象不存在,则返回defaultValue。这是一个很好的建议。不幸的是,“DoSomething()”在发生的每种情况下都是不同的事情,并且不是所有这些操作在这个接口中都有意义。另外,可能的替代方案看起来非常有趣,绝对值得一看。谢谢。你说得对,这是一种权衡,有时我们可能不愿意这样做。在这种情况下,我可能只是将if(myObject==null)改为if(myObject.IsNull),这看起来不太好,如果不是为了很多额外的好处(跨代码处理特殊情况的标准方法和减少代码重复),我可能不会这么做。