我有一个我无法弄清楚的设计问题。这就是我所拥有的:
一般来说,我有两种常见类型的对象Strikes和Options。这些已被抽象为IStrike和IOption两个接口。
让我们说IOption有以下几个字段,实际上大约有10倍,但我们可以使用以下三个来说明问题。
interface IOption
{
double Bid{get;set;}
double Ask{get;set;}
double ImpliedVol{get;set;}
}
interface IStrike
{
IOption Call{get;set;}
IOption Put{get;set;}
}
现在,这一切都很好,但是让我说我有以下方法在IOption隐含卷上执行一些“数学”
public double SquareImpliedVol(IOption opt)
{
return Math.Pow(opt.ImpliedVol,2);
}
同样,这不是问题,但是当我为我的测试编写一些模拟对象时,我不清楚我是否需要实现Bid和Ask。我没有,但我不知道,除非我知道SquareImpliedVol里面的内容,这意味着我正在编写针对代码的测试,这很糟糕。
所以为了解决这个问题,我可以创建另一个接口IOptionImpliedVol,它只包含ImpliedVol属性,然后让IOption从IOptionImpliedVol继承,就像这样
interface IOption : IOptionImpliedVol
{
double Bid{get;set;}
double Ask{get;set;}
}
然后我们可以切换SquareImpliedVol
public double SquareImpliedVol(IOptionImpliedVol opt)
{
return Math.Pow(opt.ImpliedVol,2);
}
我们很棒。我可以写模拟对象,一切都很甜蜜。除了....我想编写一个将在List上运行的方法,但我需要的唯一属性是IStrike,它是Call.ImpliedVol和Put.ImpliedVol。我想创建像
这样的东西interface IStrikeImpliedVol
{
IOptionImpliedVol Call;
IOptionImpliedVol Put;
}
然后我也可以
interface IStrike : IStrikeImpliedVol
{
IOption Call;
IOption Put;
}
除非这是不合法的。我觉得必须要有某种设计模式才能解决这个问题,但我仍然陷入某种构成和继承的网络中。
答案 0 :(得分:5)
我认为你的初始设计有两个接口是正确的。你说你必须知道何时设置Bid/Ask
以及何时不在测试中设置它并且困扰你。
让我们试着从其他方面来看待它。你正在为一些函数编写测试(假设它再次是你的SquareImpliedVol
)。您知道正确实现此功能应仅关注ImpliedVol
属性,而不关心Bid/Ask
,因此您可以将其留空。如果函数将因未设置Bid/Ask
而失败,那么您的单元测试会发现问题 - 请高兴。当然,如果空Bid/Ask
是Option
对象的错误状态,它会有所不同,但在您的情况下这似乎不是问题。
换句话说,我会说你正在针对你的知识编写一个关于特定方法应该如何工作的测试,并且这个知识与已经在该函数中编写的代码相关联这一事实并没有错
答案 1 :(得分:0)
有很多方法可以解决这个问题,但主要的问题是你期望解决方案是什么,而不是其他人如何处理它。
例如,许多人会用模拟对象来解决“测试带有IOption对象的东西”,但他们当然不会关心实际使用IOption的哪个部分。也就是说,这有点不对,因为如果你开始看一下使用模拟库来生成模拟对象的代码,他们就会清楚地知道接口的哪个部分被使用,因为他们会编写如下代码:
var mock = CREATE MOCK OF TYPE IOption
ON MOCK mock, EXPECT CALL TO ImpliedVol
FOR THAT CALL, RETURN 15
test code
VERIFY THAT CALLS WERE MADE AS EXPECTED
如果您对此感到满意,我会使用像NMock或类似的普通模拟库来为您的接口生成模拟对象。您可以通过这些图书馆说“我 NOT 期望在此代码期间调用Bid”,因此它应该适合您。
然而,你正在反对一些很难测试和量化的东西。是的,您可以验证预期的副作用确实发生了。除非您了解所有副作用,否则如何确认没有其他副作用发生?例如,如果将来有人向界面添加新方法,该怎么办?如何确保所有单元测试或其中任何单元测试或者没有单元测试验证是否从未为现有测试调用该额外方法。
重要吗?