C# 返回一个";“手动超控”;从工厂获取的值,检查null或布尔开关是否更好?
假设我们有一个类似的方法C# 返回一个";“手动超控”;从工厂获取的值,检查null或布尔开关是否更好?,c#,C#,假设我们有一个类似的方法 public static IThing getTheThing() { return internalThingGetter(); } 为了调试或单元测试的目的,我们想引入一种轻量级策略来手动覆盖它 private static IThing _thingManualOverride; public static IThing getTheThing() { if (/*some condition*/) return _thing
public static IThing getTheThing() {
return internalThingGetter();
}
为了调试或单元测试的目的,我们想引入一种轻量级策略来手动覆盖它
private static IThing _thingManualOverride;
public static IThing getTheThing() {
if (/*some condition*/)
return _thingManualOverride;
else
return internalThingGetter();
}
检查\u thingManualOverride!=null
或引入新的布尔值并检查,例如,\u应覆盖
或者,这里有更坚实的图案吗
编辑:一些需要满足的目标:
- 假设保持接口是重要的。很多代码都使用这种静态方法,修改它是一个不错的目标,但代价高昂
- 这两个检查都是“正确的”,因为如果覆盖为
,我们可以安全地假设默认路线是正确的null
class Factory{
public virtual IThing getTheThing() {
return internalThingGetter();
}
}
在测试中,使用Moq框架:
var m = new Mock<Factory>();
m.Setup(f=>f.getTheThing())
.Returns(thingManualOverride);
var m=new Mock();
m、 设置(f=>f.getTheThing())
.返回(thingManualOverride);
如果您选择\u thingManualOverride!=空
。您可以检查另一个字段,比如一个名为“\u IsthingManualLoverrideset”的布尔值,但由于程序员的错误,它们可能会给出相互冲突的值:
_thingManualOverride = null;
_IsThingManualOverrideSet = true; // <-- oops!
\u thingManualOverride=null;
_IsThingManualOverrideSet=true;//这不是一个答案,只是一个建议。阅读Roy Osherove写的第三章对你有好处(更好的是,出去买一本,读一读整本书——太棒了!)。特别是第3.4.5节(以下是包含该节的示例第3章的链接):
更新:根据您对使用静态factory类示例的请求:
static class ThingFactory
{
private static IThing _thingManualOverride = null;
public static IThing getTheThing()
{
if (_thingManualOverride != null)
return _thingManualOverride;
return new internalThingGetter();
}
public static void SetThing(IThing thing)
{
_thingManualOverride = thing;
}
}
<>我会补充说,我不喜欢使用这个模式。如果你有机会重构,考虑它可能是明智的。我不喜欢使用这个的主要原因是因为你必须记住<强>重置状态< /强>(调用带有空参数的SETTASE方法)在每次测试之后,这样您就不会用手动覆盖影响其他测试。您可以创建一个帮助器类来为您执行此操作,可能类似于:
public class OverridableValue<T>
{
private readonly Func<T> OriginalValueGetter;
private Func<T> ValueGetter;
public OverridableValue(Func<T> valueGetter)
{
this.OriginalValueGetter = valueGetter;
this.ValueGetter = OriginalValueGetter;
}
public T Value
{
get
{
return ValueGetter();
}
}
public void SetValueOverride(T value)
{
if (value == null)
ValueGetter = OriginalValueGetter;
else
ValueGetter = () => value;
}
}
您可以轻松地将null
签入SetValueOverride
替换为其他内容,或者简单地定义一个新方法来RemoveOverride()
,当null
值被视为有效覆盖时,该方法会有所帮助:
public void SetValueOverride(T value)
{
ValueGetter = () => value;
}
public void RemoveOverride()
{
ValueGetter = OriginalValueGetter;
}
另一个好处是(我认为)这种覆盖通常应该是线程安全的(您不会遇到这样的情况,即一些IsOverriddenFlag
是true
但没有覆盖值)
编辑:您可以在value
setter中进行值重写,但我认为这在实践中看起来很奇怪:
public T Value
{
get
{
return ValueGetter();
}
set
{
if (value == null)
ValueGetter = OriginalValueGetter;
else
ValueGetter = () => value;
}
}
myThing = _thingManualOverride.Value; //Thing1
_thingManualOverride.Value = new Thing2();
myThing = _thingManualOverride.Value; //Thing2
_thingManualOverride.Value = null;
myThing = _thingManualOverride.Value; //Thing1 ... not null!?
我不认为这是对他的问题的回答。我确实同意你的方法要好得多,但他并没有要求提供改进单元测试的一般提示。@MartinMulder,我认为这正是他所要求的——“或者,这里是否有更可靠的模式可供使用?”?“@alex,谢谢你,虽然我同意这对一般的单元测试来说更好,但我正试图弄清楚如果我真的需要保持当前的接口,该怎么办。因此,是的,这本身就是一个很好的答案,可能不应该得到-1,但我可能不会接受,因为它没有回答问题。对于过路人来说,这是一个有效的出发点。谢谢!我现在手头有一本。我现在会翻到3.4.5,我想这正是我想要的。如果您可以提供一些代码示例,在工厂中封装这个,并保留上面的静态接口,我将接受这个答案。我现在就开始编码!谢谢,我甚至可以提取第二个工厂,用静态方法包装它。谢谢你的警告。在我的例子中,有测试和重构的正交目标。但是其他阅读此答案的人应该知道在新代码中使用此选项的陷阱。我假设null
对于IThing\u thingManualOverride
?@ChrisSinclair极好的观点是无效的。我做了一些编辑,希望能澄清这一点。另外,当你说“轻量级策略”时,你是说它运行时高效/快速,还是说它易于编写/维护/复制?这似乎可以归结为创建一个新层来做同样的事情。这似乎把同一个问题放在了另一个地方。投票赞成强调清晰性和意图。然而,考虑到这一点,布尔值并不是那么糟糕,因为它强制了意图,但它确实复制了意图。它变成了一个双钥匙点火开关,有点干燥。
public void SetValueOverride(T value)
{
ValueGetter = () => value;
}
public void RemoveOverride()
{
ValueGetter = OriginalValueGetter;
}
public T Value
{
get
{
return ValueGetter();
}
set
{
if (value == null)
ValueGetter = OriginalValueGetter;
else
ValueGetter = () => value;
}
}
myThing = _thingManualOverride.Value; //Thing1
_thingManualOverride.Value = new Thing2();
myThing = _thingManualOverride.Value; //Thing2
_thingManualOverride.Value = null;
myThing = _thingManualOverride.Value; //Thing1 ... not null!?