2012-05-02 16 views
8

Estoy empezando a aprender sobre la IoC y la Inyección de Dependencia. Estoy planeando hacer un proyecto MonoTouch y quería usar TinyIoC, pero quería probarlo primero. Estoy creando una aplicación de consola de procesamiento de tarjetas de crédito y estoy teniendo problemas con la configuración de TinyIoC ya que tengo varias implementaciones de mi interfaz. Esta es mi aplicación de prueba.TinyIoC - Implementaciones Múltiples de la Interfaz

Interface:

public interface IPaymentProcessor 
{ 
    void ProcessPayment(string cardNumber); 
} 

dos implementaciones de la interfaz:

VisaPaymentProcessor

public class VisaPaymentProcessor : IPaymentProcessor 
{ 
    public void ProcessPayment(string cardNumber) 
    { 
     if (cardNumber.Length != 13 && cardNumber.Length != 16) 
     { 
      new ArgumentException("Card Number isn't the correct length"); 
     } 

     // some code for processing payment 
    } 
} 

AmexPaymentProcessor

public class AmexPaymentProcessor : IPaymentProcessor 
{ 
    public void ProcessPayment(string cardNumber) 
    { 
     if (cardNumber.Length != 15) 
     { 
      new ArgumentException("Card Number isn't the correct length"); 
     } 

     // some code for processing the payment 
    } 
} 

Cosas simples. Ahora tengo una clase que acepta la interfaz como parámetro en el constructor ....

CreditCardProcessor

public class CreditCardProcessor 
{ 
    public IPaymentProcessor PaymentProcessor { get; set; } 

    public CreditCardProcessor(IPaymentProcessor processor) 
    { 
     this.PaymentProcessor = processor; 
    } 

    public void ProcessPayment(string creditCardNumber) 
    { 
     this.PaymentProcessor.ProcessPayment(creditCardNumber); 
    } 
} 

Mi aplicación de consola se parece a esto ....

class Program 
{ 
    static void Main(string[] args) 
    { 
     TinyIoCContainer.Current.AutoRegister(); 

     var creditCardProcessor = TinyIoCContainer.Current.Resolve<CreditCardProcessor>(); 
     creditCardProcessor.ProcessPayment("123456789"); // 16 digits 
    } 
} 

Así que estoy tratando de averiguar cómo decirle al Resolve qué implementación de la interfaz debe pasar al constructor. Si ejecuto este código, siempre usaré la implementación VisaPaymentProcessor.

Entonces, ¿cómo puedo hacer que TinyIoC pase la implementación AmexPaymentProcessor al constructor en lugar del VisaPaymentProcessor (que parece ser el predeterminado)?

Respuesta

7

No he utilizado TinyIoC mí mismo, pero sospecho que desee:

TinyIoCContainer.Current.Register(typeof(IPaymentProcessor), 
            typeof(AmexPaymentProcessor)); 

(Si desea utilizar Amex.)

Hay varios otros Register sobrecargas disponibles, incluyendo uno que tiene un nombre para usar, que puede ser útil cuando resuelve. Realmente depende de lo que estás tratando de lograr, lo cual no está muy claro a partir de la pregunta.

+0

Gracias. Actualicé la publicación con la pregunta. Parece que 'VisaPaymentProcessor' es la implementación" predeterminada "que utiliza TinyIoC. ¿Cómo puedo hacer que TinyIoC pase la implementación de 'AmexPaymentProcessor' al constructor en su lugar. Perdón por no ser claro. –

+0

@ Eclipsed4utoo: Bien - en ese caso, esperaría que mi respuesta lo cubriera :) –

+0

@JonSkeet se disculpa por mi falta de comprensión, pero ¿cómo soluciona esto el problema original de los PO? Parece que quieren poder resolver condicionalmente las dependencias registradas en tiempo de ejecución. –

2

No estoy seguro de lo que está tratando de lograr aquí, pero si tiene múltiples implementaciones de una interfaz y quiere una específica, entonces necesita registrar cada una con un nombre, o usar RegisterMultiple, que usa el nombre de tipo para un nombre, luego resuelve usando ese nombre y usa eso junto con NamedParameterOverloads para especificar cuál quieres.

Parece más bien que es posible que desee algún tipo de ProcessorFactory, o una fachada de algún tipo, que dependa de IEnumerable y suministra/actúa como una fachada para la implementación correcta según el número ingresado.

+1

Leí esto sobre Windsor IoC y me preguntaba si lo mismo es cierto para TinyIoC .... 'Ahora cuando" Resolve "una instancia de CreditCardProcessor, el contenedor mira al constructor y ve que necesita un IPaymentProcessor. El contenedor busca ver si ese tipo está registrado en el contenedor. Si es así, el contenedor "Resolverá" ese tipo y luego instanciará CreditCardProcessor. En caso afirmativo, ¿cómo maneja TinyIoC cuando hay dos implementaciones registradas para la interfaz? Parece elegir el primer tipo registrado al usar 'RegisterMultiple'. –

+1

@ Eclipsed4utoo Solo hay un registro "predeterminado" para una interfaz (una sin nombre), y esa es la que obtendrás si dependes de ella. Cuando usa el Autoregistro, que es el "predeterminado", no está garantizado, por lo que debe configurarlo usted mismo, o registrarlos todos y extraer el correcto por su nombre. –

+0

Gracias Steven. La única forma en que puedo encontrar para especificar la implementación correcta es usar NamedParameterOverloads cuando resuelvo el 'CreditCardProcessor'. Sin embargo, eso parecería "hardcode" la firma del constructor ya que necesitaría NamedParametersOverload para coincidir con la firma del constructor. Si el constructor cambia, NamedParametersOverload ahora fallará si no hago los cambios allí. Para mí, ese tipo de derrotas tiene el propósito de usar un IoC. ¿Estoy solo fuera de la base? –

2

Algo así en Global.asax o entrada de la aplicación (Modificado por su ejemplo)

 const string nameTrim = "paymentprocessor"; 
     var type = typeof(IPaymentProcessor); 
     AppDomain.CurrentDomain.GetAssemblies() 
      .SelectMany(s => s.GetTypes()) 
      .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList() 
      .ForEach(t => 
      { 
       var name = t.Name.ToLower(); 
       if (name.EndsWith(nameTrim)) 
        name = name.Substring(0, name.Length - nameTrim.Length); 

       TinyIoCContainer.Current.Register(type, t, name); 
      }); 

Se encuentra alla implementaciones de IPaymentProcessor y los registra con el nombre de clase (-PaymentProcessor, si el nombre de la clase termina con PaymentProcessor)

Entonces puedo resolver, por ejemplo, "AmexPaymentProcessor" con

 IPaymentProcessor handler; 
     if (TinyIoCContainer.Current.TryResolve("amex", out handler)) 
     { 
      response = handler.ProcessPayment(cardNumber); 
     } 
Cuestiones relacionadas