TDD&;设计建议

本文关键字:amp TDD | 更新日期: 2023-09-27 18:28:19

我有一个方法,它调用Action<string>

public Action<string> DisplayError;
public void MyMethod()
{
  DisplayError("ERROR");
}

在这个方法中,我想调用DisplayError,但我可以看到,如果DisplayError为null,它将抛出一个异常。

我可以运行一个测试来证明它会抛出异常。

所以我知道我想在我的代码中添加一个if (DisplayError != null),但我觉得这个设计在某种程度上是错误的。也许测试应该有所不同?

TDD&;设计建议

与其将Action作为公共字段,不如将其作为属性或传递到方法中。然后,您可以在这一点上进行null检查。我也怀疑你需要在设置后检索它,所以要么:

_displayError = (s) => { throw new ArgumentNullException("Please tell me what to do with this exception!"); };
public Action<string> DisplayError
{
    set 
    {
        if (value == null) { throw new ArgumentNullException("I need this!"); }
        _displayError = value;
    }
}
public void MyMethod()
{
    _displayError("ERROR");
}

或者:

public void MyMethod(Action<string> displayError)
{
    if (displayError == null) { throw new ArgumentNullException("I need this!"); }
    displayError("ERROR");
}

其中的第一个不需要消费者对代码进行任何更改。

当然,如果您只是与使用您的代码的团队合作,则根本不需要进行null检查。只要找出谁在错误地使用你的代码,并进行友好的聊天,找出误解所在。防御性编程不能很好地替代成员相互信任的团队,空检查也不如拥有一个定义明确、易于使用的界面有效。

你需要两个测试:

我的班级:

  • 应该允许使用者处理任何错误
    • 假设我将因任何原因产生错误(在此处设置上下文)
    • 当我产生错误时(调用方法,让错误处理程序设置一些变量)
    • 然后我应该将错误提供给处理程序(检查设置的变量)
  • 应该确保附加了错误处理程序
    • 假设我可能会产生错误(你可以在这里编写你喜欢的代码,因为它无关紧要)
    • 当使用者在没有错误处理程序的情况下调用我时(使用null错误处理程序调用方法)
    • 然后我应该立即要求消费者不要这样做(检查是否有异常)

它完全取决于上下文。

此类的用户是否需要根据合同设置值?然后它应该抛出一个异常。

它是可选的吗?那么你的检查是有效的,但如果你只是将DisplayError默认为空Action,你会得到类似的行为

DisplayError = s => {};

您的方法还导致用户可以DisplayError设置为null,在这种情况下,只有您才能决定什么是有效的。如果您将其设置为属性而不是字段,您将为自己提供更多选择。

_displayError = s => {};
public Action<string> DisplayError
{
   get { return _displayError; }
   set 
   { 
       _displayError = value ?? (s => {}); 
       /* or throw on null */
   }
}

我相信是在Steve Yeggie的一次演讲中,我听到了这一点:"虽然静态类型语言确实可以保护您在编译时免受一系列错误的影响,但它们并不能否定null检查的必要性。"

我觉得这很正常。

我只是在喝早上的咖啡,所以如果我完全错过了目标,请告诉我。