2010-07-26 13 views
5

Tengo problemas para ordenar una forma de gestionar las dependencias automáticas resueltas y manuales en mis clases.Resolviendo dependencias automáticas y manuales

Digamos que tengo dos clases para calcular los precios: una calcula cuánto cobraré por el envío y la otra cuánto cobraré por todo el pedido. El segundo usa el primero para sumar el precio de envío al precio total de la orden.

Ambas clases tienen una dependencia a una tercera clase que llamaré ExchangeRate que me da la tasa de cambio que debería usar para el cálculo del precio.

Hasta el momento tenemos esta cadena de dependencia:

OrderCalculator -> ShippingCalculator -> cambiaria

estoy usando Ninject para resolver estas dependencias y esto estaba trabajando hasta ahora. Ahora tengo el requisito de que la tasa devuelta por la clase ExchangeRate variará según un parámetro que se proporcionará en el Constructor (porque el objeto no funcionará sin esto, por lo tanto, para hacer que la dependencia sea explícita se coloca en el constructor) de una entrada de usuario. Debido a eso, ya no puedo resolver mis dependencias automáticamente.

Cada vez que quiero el OrderCalculator o cualquier otra clase que dependa de ExchangeRate no puedo pedirle al contenedor de Ninject que me lo resuelva ya que necesito proporcionar el parámetro en el constructor.

¿Qué sugieres en este caso?

Gracias!

EDIT: Vamos a añadir algo de código

Esta cadena de objetos es consumido por un servicio WCF y estoy usando Ninject como el contenedor DI.

 
public class OrderCalculator : IOrderCalculator 
{ 
     private IExchangeRate _exchangeRate; 
     public OrderCalculator(IExchangeRate exchangeRate) 
     { 
       _exchangeRate = exchangeRate; 
     } 
     public decimal CalculateOrderTotal(Order newOrder) 
     { 
       var total = 0m; 
       foreach(var item in newOrder.Items) 
       { 
         total += item.Price * _exchangeRate.GetRate(); 
       } 
       return total;    
     } 
} 

public class ExchangeRate : IExchangeRate 
{ 
     private RunTimeClass _runtimeValue; 
     public ExchangeRate(RunTimeClass runtimeValue) 
     { 
       _runtimeValue = runtimeValue; 
     } 
     public decimal GetRate() 
     { 
       //returns the rate according to _runtimeValue 
       if(_runtimeValue == 1) 
         return 15.3m; 
       else if(_runtimeValue == 2) 
         return 9.9m 
       else 
         return 30m; 
     } 
} 

//WCF Service 
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue) 
{ 
    //I would like to pass the runtimeValue when resolving the IOrderCalculator depedency using a dictionary or something 
    //Something like this ObjectFactory.Resolve(runtimeValue); 
    IOrderCalculator calculator = ObjectFactory.Resolve();  
    return calculator.CalculateOrderTotal(newOrder);  
} 

Respuesta

1

Terminé haciendo algo totalmente diferente.

Antes de llamar a ObjectFactory para resolver las dependencias, creo una nueva instancia de IExchangeRate utilizando runTimeValue y le digo al contenedor IoC/DI que lo use en lugar de crear uno nuevo. De esta forma, se conserva toda la cadena de objetos y no hay necesidad de fábricas.


//WCF Service 
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue) 
{ 
    IExchangeRate ex = new ExchangeRate(runtimeValue); 
    IOrderCalculator calculator = ObjectFactory.With<IExchangeRate>(ex).GetInstance(); 
    return calculator.CalculateOrderTotal(newOrder);  
} 

Pero desde Ninject no tiene manera de hacer esto (sólo REBIND que no es lo que quiero) me cambió mi contenedor a StructureMap.

Gracias chicos por toda su ayuda! ¡Realmente lo aprecio!

0

Mueva esa inicialización fuera del constructor.

7

Como siempre, cuando tiene una dependencia parcial de un valor de tiempo de ejecución, the solution is an Abstract Factory.

Algo como esto debería funcionar:

public interface IExchangeRateFactory 
{ 
    ExchangeRate GetExchangeRate(object runTimeValue); 
} 

A continuación, inyecte una IExchangeRateFactory en sus consumidores en lugar de de tipos de cambio y utilizar el método GetExchangeRate para convertir el valor de tiempo de ejecución de una instancia de tipos de cambio.

Obviamente, también deberá proporcionar una implementación de IExchangeRateFactory y configurar NInject para asignar la interfaz a su implementación.

+0

Veamos si entendí. Mi OrderCalculator/ShippingCalculator debe recibir un objeto (IExchangeRateFactory) que sea capaz de construir la clase ExchangeRate y llamar a GetExchangeRate pasando el parámetro runtime, ¿verdad? En caso afirmativo, entonces el parámetro runTimeValue se convertirá en algo que debería preocupar a OrderCalculator/ShippingCalculator y me gustaría que no lo hiciera. – tucaz

+0

Si * ninguno * de esos debería tratar con el valor del tiempo de ejecución, ¿de dónde provendría? –

+0

Entiendo lo que dices, pero si me gusta eso, entonces algo (runTimeValue) que era solo una dependencia de ExchangeRate también se convertirá en una dependencia de cualquier otra clase que lo use. Estoy buscando una manera de pasar este runTimeValue al DI Container y dejar que se encargue de la creación del objeto. – tucaz

1

Puede encontrar inyectando factory delegates o providers para ser útil. Ambos son más o menos lo mismo que la respuesta de Mark (+ 1d).

0

actualiza basándose en el código:

//WCF Service 
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue) 
{ 
    //I would like to pass the runtimeValue when resolving the IOrderCalculator depedency using a dictionary or something 
    //Something like this ObjectFactory.Resolve(runtimeValue); 
    IOrderCalculator calculator = ObjectFactory.Resolve();  
    return calculator.CalculateOrderTotal(newOrder);  
} 

Aviso cómo este método sólo quiere runtimeValue para que pueda pasar a otra cosa? Esto está sucediendo porque la construcción del objeto y las responsabilidades del tiempo de ejecución son mixtas. Creo que deberías estar pidiendo un IOrderCalculator en el constructor para este método.

Así que este método solo se convierte en:

//WCF Service 
public decimal GetTotalForOrder(Order newOrder, IOrderCalculator calculator) 
{ 
    return calculator.CalculateOrderTotal(newOrder);  
} 

Ahora, cuando usted construye su IOrderCalculator debe pasar runtimeValue a ella de constructor. Es un poco difícil de responder cuando no sabemos qué es runtimeValue o de dónde viene.

+0

Funciona, pero este parámetro también se convertiría en una dependencia de quien usa ExchangeRate cuando no es verdadero. – tucaz

+0

runTimeValue es un parámetro INT con un ID de país/estado/ciudad que se utiliza para determinar la tasa de impuestos que se utilizará y proviene de la llamada al método WCF. Es por eso que solo está disponible en tiempo de ejecución y también por qué no puedo usarlo en la creación del servicio WCF para construir IOrderCalculator. Solo está disponible después de que se construye WCF. – tucaz

+0

En ese caso, la respuesta de Mark Seemanns es correcta. –