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

本文关键字:结果 重构 Null 模式 对象 | 更新日期: 2023-09-27 18:06:59

我正在重构一个大的类,这个类有很多检查null的地方,使用null对象模式。到目前为止,这是一个几乎顺利的变化,但我对最终结果有几个问题,我想知道是否有更好或不同的方法,甚至回到原来的方式。

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

IMyObject myObject = GetMyObject();
if(myObject != null && !myObject.BooleanProperty)
   DoSomething();

所以你可以看到,我可能会从这个条件中删除空检查,但我有一个布尔属性,如果设置为默认值,将执行一段代码。如果我总是返回true,我可能会引入难以发现和消除的细微错误。

另一个问题是我必须像这样修改null检查:

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

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

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

就是这样了。在这种情况下,null对象模式是否比到处都是意大利式null检查更糟糕?有没有更好的方法来做到这一点?

谢谢你的回答。

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

重构你的代码,让DoSomething()是Null对象上的方法,并简单地实现为无操作,会更好吗?Null对象的另一个替代方案是Maybe<T>

Null对象模式是关于一种权衡——您从消除Null检查中获益,但需要另一个类来维护。

我建议添加一个IsNull布尔属性到你的接口/基类。

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

这允许您避免针对确切类型进行测试,并且更清楚地表明您的意图。您还可以将其用作处理日期的代码中的测试,允许您保留不可空的日期属性。

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

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

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

它允许您编写这样的代码,甚至扩展接口:

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

你应该在所有输入到系统的数据上实现条件,即所有的公共方法,并防止开发人员编写违反这一点的代码。当它是由于bug而发生时,异常和堆栈跟踪会告诉您问题的确切位置。

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