Разрешение автоматических и ручных зависимостей - PullRequest
5 голосов
/ 26 июля 2010

У меня возникли небольшие проблемы с сортировкой способа управления автоматически разрешаемыми и ручными зависимостями в моих классах.

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

Оба класса имеют зависимость от третьего класса, который я назову ExchangeRate, который дает мне курс обмена, который я должен использовать для расчета цены,

Пока у нас есть эта цепочка зависимостей:

OrderCalculator -> ShippingCalculator -> ExchangeRate

Я использую Ninject для разрешения этих зависимостей, и это работало до сих пор.Теперь у меня есть требование, чтобы скорость, возвращаемая классом ExchangeRate, изменялась в зависимости от параметра, который будет предоставлен в Конструкторе (поскольку объект не будет работать без этого, поэтому, чтобы сделать зависимость явной, он будет помещен в конструктор).из пользовательского ввода.Из-за этого я больше не могу автоматически разрешать свои зависимости.

Всякий раз, когда я хочу использовать OrderCalculator или любые другие классы, которые зависят от ExchangeRate, я не могу попросить контейнер Ninject разрешить его мне, поскольку мне нужно предоставить параметр в конструкторе.

Что делатьвы предлагаете в этом случае?

Спасибо!

РЕДАКТИРОВАТЬ: Давайте добавим немного кода

Эта цепочка объектов используется службой WCF, и яЯ использую Ninject в качестве контейнера 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);    
}

Ответы [ 5 ]

7 голосов
/ 26 июля 2010

Как всегда, когда у вас есть частичная зависимость от значения времени выполнения, решением является абстрактная фабрика .

Как-то так должно работать:

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

Теперь добавьте IExchangeRateFactory в ваших потребителей вместо ExchangeRate и используйте метод GetExchangeRate для преобразования значения времени выполнения в экземпляр ExchangeRate.

Очевидно, что вам также потребуется предоставить реализацию IExchangeRateFactory и настроить NInject для сопоставления интерфейса с вашей реализацией.

1 голос
/ 30 июля 2010

В итоге я сделал что-то совершенно другое.

Прежде чем вызвать ObjectFactory для разрешения зависимостей, я создаю новый экземпляр IExchangeRate, используя runTimeValue, и сообщаю контейнеру IoC / DI использовать его вместо создания нового. Таким образом, сохраняется вся цепочка объектов и нет необходимости в фабриках.


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

Но так как у Ninject нет способа сделать это (только Rebind, а это не то, что я хочу), я изменил свой контейнер на StructureMap.

Спасибо, ребята, за вашу помощь! Очень ценю это!

1 голос
/ 27 июля 2010

Может оказаться полезным использование фабричных делегатов или поставщиков . Оба в значительной степени подразумевают ответ Марка (+ 1d).

0 голосов
/ 27 июля 2010

Обновлено на основе кода:

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

Обратите внимание, что этот метод требует только runtimeValue, чтобы он мог передавать его чему-то другому? Это происходит из-за того, что ответственность за строительство объекта и время выполнения смешана. Я думаю, что вы должны спросить IOrderCalculator в конструкторе для этого метода.

Так что этот метод просто становится:

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

Теперь, когда вы создаете свой IOrderCalculator, вы должны передать runtimeValue его конструктору. Трудно ответить, когда мы не знаем, что такое runtimeValue или откуда оно взято.

0 голосов
/ 26 июля 2010

Переместите эту инициализацию из конструктора.

...