Can I combine composition and inheritance with interfaces in C#
I have a design problem that I can't figure out. Here's what I've got:
In general, I have two general types of objects Strikes and Options. These have been abstracted into two interfaces IStrike and IOption.
Let's say that IOption has the following fields, in reality there are about 10 times as many, but we can use the following three to illustrate the problem.
interface IOption
{
double Bid{get;set;}
double Ask{get;set;}
double ImpliedVol{get;set;}
}
interface IStrike
{
IOption Call{get;set;}
IOption Put{get;set;}
}
Now, that's all well and good, but let's say I've got the following method for performing some "math" on the IOption implied vol
public double SquareImpliedVol(IOption opt)
{
return Math.Pow(opt.ImpliedVol,2);
}
Again, not a problem, but when I'm writing some mock objects for my tests, it's not clear to me if I need to implement Bid and Ask. I don't, but I wouldn't know that unless I knew the guts inside SquareImpliedVol, which means I'm writing tests against the code, which is bad.
So to fix this, I could create another interface IOptionImpliedVol that just contains the ImpliedVol property, and then have IOption inherit from IOptionImpliedVol like so
interface IOption : IOptionImpliedVol
{
double Bid{get;set;}
double Ask{get;set;}
}
And then we can switch up SquareImpliedVol
public double SquareImpliedVol(IOptionImpliedVol opt)
{
return Math.Pow(opt.ImpliedVol,2);
}
And we're great. I can write mock objects and everything is sweet. Except....I want to write a method that is going to operate on a List, but the only properties I need out of IStrike are the Call.ImpliedVol and Put.ImpliedVol. I want to create something like
interface IStrikeImpliedVol
{
IOptionImpliedVol Call;
IOptionImpliedVol Put;
}
and then I could also have
interface IStrike : IStrikeImpliedVol
{
IOption Call;
IOption Put;
}
Except that isn't legal. I feel like there has to be some kind of design pattern tha开发者_Go百科t I could to work this out, but I'm stuck in some kind of web of composition and inheritance.
I think you're initial design with two interfaces was correct. You're saying that you have to know when to set Bid/Ask
and when not to set it in your tests and that bothers you.
Let's try to look at it from other point. You're writing a test for some function (let's say it is your SquareImpliedVol
again). You know that correctly implemented this function should care only about ImpliedVol
property, but not about Bid/Ask
so you can leave those blank. If function will fail with unset Bid/Ask
then your unit-test found an issue - be happy. Of course it differs if empty Bid/Ask
is incorrect state of Option
object, but that doesn't seem to be an issue in your case.
In other words I would say you're writing a test against your knowledge on how particular method should work and there is nothing wrong in the fact that this knowledge correlates with code already written in that function.
There's many ways to handle this, but the main problem is what you expect the solution to be, not how anyone else has handled it.
For instance, many would solve the "test something that takes an IOption object" with mocking objects, but they would of course not be anywhere near caring what part of IOption is actually used. That is, that's slightly wrong, since if you start taking a look at code that uses mocking libraries to produce mocking objects, they clearly know what part of the interface is used, since they will write code that looks like this:
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
If you're satisfied with that, I would just use a normal mocking library like NMock or similar, to produce the mocking objects for your interfaces. You can, with those libraries, also say that "I do NOT expect a call to Bid during this code", so it should work for you.
However, you're butting against something that is quite hard to test and quantify. Yes, you can verify that expected side-effects did indeed happen. How do you verify that no other side-effects did happen unless you know about them all? For instance, what if, in the future, someone adds a new method to the interface. How can you make sure all unit-tests, or any of them, or none, verify that a call to that extra method was never called for existing tests.
Does it matter?
精彩评论