Resolving automatic and manual dependencies
I´m having a little bit of trouble sorting a way to manage automatic resolved and manual dependencies in my classes.
Let´s say I have two classes to calculate prices: one calculates how much I will charge for shipping and the other how much I will charge for the entire order. The second uses the first in order to sum the shipping price to the entire order price.
Both classes have a dependency to a third class that I will call ExchangeRate which gives me the exchange rate I should use for price calculation.
So far we have this chain of dependency:
OrderCalculator -> ShippingCalculator -> ExchangeRate
I´m using Ninject to resolve these dependencies and this was working until now. Now I have a requirement that the rate returned by ExchangeRate class will vary upon a parameter that will be provided in the Constructor (because the object won´t work without this, so to make the dependency explicit it´s placed on the constructor) coming from a user input. Because of that I can no longer resolve my dependencies automatically.
Whenever I want to the OrderCalculator or any other classes that depends on ExchangeRate I cannot ask the Ninject container to resolve it to me since I need to provide the parameter in the constructor.
What do u suggest in this case?
Thanks!
EDIT: Let's add some code
This chain of objects is consumed by a WCF service and开发者_如何学Python I'm using Ninject as the DI container.
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); }
As always, when you have a partial dependency on a run-time value, the solution is an Abstract Factory.
Something like this should work:
public interface IExchangeRateFactory
{
ExchangeRate GetExchangeRate(object runTimeValue);
}
Now inject an IExchangeRateFactory into your consumers instead of ExchangeRate and use the GetExchangeRate method to convert the run-time value to an ExchangeRate instance.
Obviously you will also need to provide an implementation of IExchangeRateFactory and configure NInject to map the interface to your implementation.
You may find injecting factory delegates or providers to be useful. Both are pretty much impls of Mark's (+1d) answer.
I ended up doing something totally different.
Before I call the ObjectFactory to resolve the dependencies for me I create a new instance of the IExchangeRate using the runTimeValue and tell the IoC/DI container to use it instead of creating a new one. This way the whole chain of objects is preserved and there is no need for factories.
//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);
}
But since Ninject doesn't have a way to do this (only Rebind which is not what I want) I changed my container to StructureMap.
Thanks guys for all your help! Really appreciate it!
Move that initialization out of the constructor.
Updated based on the code:
//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);
}
Notice how this method only wants runtimeValue so that it can pass it to something else? This is happening because object construction and runtime responsibilities are mixed. I think you should be asking for a IOrderCalculator in the constructor for this method.
So this method just becomes:
//WCF Service
public decimal GetTotalForOrder(Order newOrder, IOrderCalculator calculator)
{
return calculator.CalculateOrderTotal(newOrder);
}
Now, when you construct your IOrderCalculator you should pass runtimeValue to it's constructor. It's a bit hard to answer when we do not know what runtimeValue is or where it comes from.
精彩评论