2010-07-26 26 views
5

Ho un po 'di problemi nell'ordinare un modo per gestire le dipendenze automatiche risolte e manuali nelle mie classi.Risoluzione delle dipendenze automatiche e manuali

Diciamo che ho due classi per calcolare i prezzi: uno calcola quanto addebiterò per la spedizione e l'altro quanto addebiterò per l'intero ordine. Il secondo utilizza il primo per sommare il prezzo di spedizione all'intero prezzo dell'ordine.

Entrambe le classi hanno una dipendenza da una terza classe che chiamerò ExchangeRate che mi dà il tasso di cambio che dovrei usare per il calcolo del prezzo.

Finora abbiamo questa catena di dipendenza:

OrderCalculator -> ShippingCalculator -> ExchangeRate

sto usando Ninject per risolvere queste dipendenze e questo stava lavorando fino ad ora. Ora ho il requisito che la velocità restituita dalla classe ExchangeRate varierà su un parametro che verrà fornito nel Costruttore (perché l'oggetto non funzionerà senza questo, quindi per rendere esplicita la dipendenza sul costruttore) in arrivo da un input dell'utente. Per questo motivo non posso più risolvere automaticamente le mie dipendenze.

Ogni volta che desidero OrderCalculator o qualsiasi altra classe che dipende da ExchangeRate, non posso chiedere al contenitore Ninject di risolverlo poiché è necessario fornire il parametro nel costruttore.

Che cosa suggerisci in questo caso?

Grazie!

EDIT: Aggiungiamo un certo codice

Questa catena di oggetti viene consumato da un servizio WCF e sto usando Ninject come contenitore 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);  
} 

risposta

1

Ho finito per fare qualcosa di completamente diverso.

Prima di chiamare ObjectFactory per risolvere le dipendenze per me, creo una nuova istanza di IExchangeRate utilizzando runTimeValue e comunico al contenitore IoC/DI di utilizzarlo invece di crearne uno nuovo. In questo modo viene preservata l'intera catena di oggetti e non c'è bisogno di fabbriche.


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

Ma poiché Ninject non hanno un modo per fare questo (solo associare nuovamente che non è quello che voglio) Ho cambiato il mio contenitore per StructureMap.

Grazie ragazzi per tutto il vostro aiuto! Lo apprezzo davvero!

0

Spostare l'inizializzazione fuori dal costruttore.

7

Come sempre, quando si ha una dipendenza parziale su un valore di runtime, the solution is an Abstract Factory.

Qualcosa del genere dovrebbe funzionare:

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

punto, iniettare un IExchangeRateFactory nei vostri consumatori, invece di ExchangeRate e utilizzare il metodo GetExchangeRate per convertire il valore di run-time a un'istanza ExchangeRate.

Ovviamente sarà inoltre necessario fornire un'implementazione di IExchangeRateFactory e configurare NInject per mappare l'interfaccia alla propria implementazione.

+0

Vediamo se ho capito. My OrderCalculator/ShippingCalculator deve ricevere un oggetto (IExchangeRateFactory) in grado di creare la classe ExchangeRate e chiamare GetExchangeRate che passa il parametro runtime, giusto? Se sì, il parametro runTimeValue diventerà qualcosa a cui OrderCalculator/ShippingCalculator dovrebbe interessare e vorrei che non si verificasse. – tucaz

+0

Se * nessuno * di quelli dovrebbe occuparsi del valore di runtime, da dove proviene? –

+0

Capisco cosa stai dicendo, ma se lo faccio, allora qualcosa (runTimeValue) che era solo una dipendenza di ExchangeRate diventerà anche una dipendenza da qualunque altra classe lo usi. Sto cercando un modo per passare questo runTimeValue al contenitore DI e lasciare che si occupi della creazione dell'oggetto. – tucaz

0

aggiornata sulla base del codice:

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

Avviso come questo metodo vuole solo runtimeValue in modo che possa passare a qualcos'altro? Questo sta accadendo perché la costruzione dell'oggetto e le responsabilità di runtime sono miste. Penso che dovresti chiedere un IOrderCalculator nel costruttore per questo metodo.

Quindi questo metodo diventa solo:

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

Ora, quando si costruisce la vostra IOrderCalculator si dovrebbe passare ad esso runtimeValue del costruttore. È un po 'difficile rispondere quando non sappiamo cosa sia runtimeValue o da dove provenga.

+0

Funziona, ma questo parametro diventerebbe anche una dipendenza da chiunque stia utilizzando ExchangeRate quando non è vero. – tucaz

+0

runTimeValue è un parametro INT con un ID paese/stato/città utilizzato per determinare l'aliquota fiscale da utilizzare e proveniente dalla chiamata al metodo WCF. Ecco perché è disponibile solo in runtime e anche perché non posso usarlo nella creazione del servizio WCF per costruire IOrderCalculator. È disponibile solo dopo la creazione del WCF. – tucaz

+0

In tal caso, la risposta di Mark Seemann è giusta. –

Problemi correlati