2011-03-18 12 views
5

? Tengo un problema de diseño que no puedo entender. Esto es lo que tengo:¿Puedo combinar composición y herencia con interfaces en C#

En general, tengo dos tipos generales de objetos Huelgas y opciones. Estos han sido resumidos en dos interfaces IStrike y IOption.

Digamos que IOption tiene los siguientes campos, en realidad hay alrededor de 10 veces más, pero podemos usar los siguientes tres para ilustrar el problema.

interface IOption 
{ 
    double Bid{get;set;} 
    double Ask{get;set;} 
    double ImpliedVol{get;set;} 
} 


interface IStrike 
{ 
    IOption Call{get;set;} 
    IOption Put{get;set;} 
} 

Ahora, eso es todo muy bien, pero digamos que tengo el siguiente método para realizar un poco de "matemáticas" en el vol iOption implícita

public double SquareImpliedVol(IOption opt) 
{ 
    return Math.Pow(opt.ImpliedVol,2); 
} 

Una vez más, no es un problema, pero cuando estoy escribiendo algunos objetos simulados para mis pruebas, no tengo claro si necesito implementar Bid and Ask. No, pero no lo sabría a menos que conociera las agallas dentro de SquareImpliedVol, lo que significa que estoy escribiendo pruebas contra el código, lo cual es malo.

Así que para solucionar este problema, podría crear otro IOptionImpliedVol interfaz que solo contiene la propiedad ImpliedVol, y luego tiene iOption hereda de IOptionImpliedVol al igual que

interface IOption : IOptionImpliedVol 
{ 
    double Bid{get;set;} 
    double Ask{get;set;} 
} 

y luego podemos cambiar hasta SquareImpliedVol

public double SquareImpliedVol(IOptionImpliedVol opt) 
{ 
    return Math.Pow(opt.ImpliedVol,2); 
} 

Y estamos geniales. Puedo escribir objetos simulados y todo es dulce. Excepto .... Quiero escribir un método que operará en una lista, pero las únicas propiedades que necesito de IStrike son Call.ImpliedVol y Put.ImpliedVol. Quiero crear algo así como

interface IStrikeImpliedVol 
{ 
    IOptionImpliedVol Call; 
    IOptionImpliedVol Put; 
} 

y luego yo también podría tener

interface IStrike : IStrikeImpliedVol 
{ 
    IOption Call; 
    IOption Put; 
} 

Excepto que no es legal. Siento que tiene que haber algún tipo de patrón de diseño para resolver esto, pero estoy atrapado en una especie de red de composición y herencia.

Respuesta

5

Creo que su diseño inicial con dos interfaces era correcto. Usted dice que debe saber cuándo configurar Bid/Ask y cuándo no configurarlo en sus pruebas, y eso le molesta.

Tratemos de verlo desde otro punto. Estás escribiendo una prueba para alguna función (digamos que es tu SquareImpliedVol otra vez). Usted sabe que esta función implementada correctamente debe preocuparse solo por la propiedad ImpliedVol, pero no por Bid/Ask, por lo que puede dejarla en blanco. Si la función fallará con unset Bid/Ask, entonces su unidad de prueba encontró un problema: sea feliz. Por supuesto, es diferente si el Bid/Ask está incorrecto en el estado del objeto Option, pero eso no parece ser un problema en su caso.

En otras palabras, diría que está escribiendo una prueba en contra de su conocimiento sobre cómo debe funcionar el método particular y no hay nada de malo en el hecho de que este conocimiento se correlaciona con el código ya escrito en esa función.

+2

1 a la idea de que el diseño inicial era correcta. Estoy bastante seguro de que * no * quiere ir con las interfaces de '' IOptionImpliedVol' y IStrikeImpliedVol'; ¿Cuando terminará? Yousef (Jonathan) ya dio un término muy apropiado para describir el camino que esto lleva a la derrota: una * web * desordenada y complicada. Escucha a Snowbear. –

+0

Gracias. A veces puedo ser un poco pedante con el diseño, y es bueno tener una comunidad como StackOverflow hablar cuando pienso que podría estar sucediendo. –

0

Hay muchas maneras de manejar esto, pero el problema principal es lo que usted espera que sea la solución, no cómo alguien más la ha manejado.

Por ejemplo, muchos resolverían la "prueba de algo que lleva un objeto IOption" con objetos burlones, pero por supuesto no estarían ni cerca de preocuparse de qué parte de IOption se utiliza realmente. Es decir, eso es un poco mal, ya que si comienza a tomar un vistazo a código que utiliza burlarse de bibliotecas para producir burlarse de objetos, saben claramente qué parte de la interfaz se utiliza, ya que van a escribir código que se parece a esto:

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 

Si está satisfecho con eso, sólo sería utilizar una biblioteca de burla normal como NMock o similar, para producir los objetos de burla para sus interfaces. Puede, con esas bibliotecas, también decir que "yo no esperar una llamada a hacer una oferta en este código", por lo que debe trabajar para usted.

Sin embargo, estás tope contra algo que es muy difícil de probar y cuantificar. Sí, puedes verificar que los efectos secundarios esperados ocurrieron. ¿Cómo verificas que no haya otros efectos secundarios a menos que los conozcas todos? Por ejemplo, ¿qué pasa si, en el futuro, alguien agrega un nuevo método a la interfaz. ¿Cómo puede asegurarse de que todas las pruebas de unidad, o cualquiera de ellas, o ninguna, verifiquen que nunca se haya llamado a una llamada a ese método adicional para las pruebas existentes?

¿Importa?

Cuestiones relacionadas