Могу ли я объединить композицию и наследование с интерфейсами в C # - PullRequest
5 голосов
/ 18 марта 2011

У меня проблема с дизайном, которую я не могу понять. Вот что у меня есть:

В общем, у меня есть два основных типа объектов 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 vol

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;
}

За исключением того, что это не законно. Я чувствую, что должен быть какой-то шаблон проектирования, который я мог бы решить, но я застрял в какой-то паутине композиции и наследования.

Ответы [ 2 ]

5 голосов
/ 18 марта 2011

Я думаю, что ваш первоначальный дизайн с двумя интерфейсами был правильным.Вы говорите, что должны знать, когда устанавливать Bid/Ask, а когда не устанавливать его в своих тестах, и это вас беспокоит.

Попробуем взглянуть на это с другой стороны.Вы пишете тест для некоторой функции (допустим, это снова ваш SquareImpliedVol).Вы знаете, что правильно реализованная функция должна заботиться только о свойстве ImpliedVol, но не о Bid/Ask, поэтому вы можете оставить их пустыми.Если функция завершится с ошибкой Bid/Ask, тогда ваш юнит-тест обнаружил проблему - будьте счастливы.Конечно, это отличается, если пустое Bid/Ask является неправильным состоянием Option объекта, но это не кажется проблемой в вашем случае.

Другими словами, я бы сказал, что вы пишете тест против ваших знаний о том, как должен работать конкретный метод, и нет ничего плохого в том, что эти знания коррелируют с кодом, уже написанным вэта функция.

0 голосов
/ 18 марта 2011

Есть много способов справиться с этим, но главная проблема в том, что вы ожидаете от решения, а не в том, как кто-то другой справился с этим.

Например, многие решали бы «тестирование чего-либо, принимающего объект 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 или аналогичную, для создания объектов-насмешек для ваших интерфейсов. С этими библиотеками вы также можете сказать, что «я НЕ ожидаю вызова Bid во время этого кода», поэтому он должен работать для вас.

Тем не менее, вы сталкиваетесь с чем-то, что довольно сложно проверить и измерить. Да, вы можете проверить, что ожидаемые побочные эффекты действительно имели место. Как вы проверяете, что никаких других побочных эффектов не было, если вы не знаете обо всех них? Например, что, если в будущем кто-то добавит новый интерфейс к интерфейсу. Как вы можете убедиться, что все юнит-тесты, или любой из них, или ни одного, проверяют, что вызов этого дополнительного метода никогда не вызывался для существующих тестов.

Имеет ли это значение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...