2011-12-21 11 views
11

Estoy implementando el patrón de diseño del constructor para construir diferentes tipos de objetos de gráfico que se mostrarán en una interfaz de usuario de WPF. Estoy usando Ninject como mi contenedor IOC. Sin embargo, estoy tratando de encontrar una solución extensible elegante.Inyectar diferentes clases que implementan la misma interfaz utilizando Ninject

Tengo un objeto ChartDirector que toma un IChartBuilder como una dependencia. También tengo TemperatureChartBuilder y ThresholdChartBuilder que implementan IChartBuilder. Quiero inyectar TemperatureChartBuilder O ThresholdChartBuilder en ChartDirector dependiendo de un evento que se dispare o dependiendo de una llamada del cliente. He ilustrado mi problema a continuación en el código.

// ChartDirector also depends on this 
kernel.Bind<IExample>().To<Example>(); 

// when called in Method X... 
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>(); 

// when called in Method Y... 
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder(); 

// TemperatureChartBuilder is a dependency of ChartDirector, need a way to dynamically 
// allocate which binding to use. 
var director = kernel.Get<ChartDirector>(); 

// without Ninject I would do 
var director = new ChartDirector(new TemperatureChartBuilder); 

// or 
var director = new ChartDirector(new ThresholdChartBuilder); 

EDIT:

Junto con la respuesta de Gary, y observando una ligera edición que ChartDirector tiene otra dependencia, ahora quiero hacer algo como esto:

var director = kernel.Get<ChartDirector>().WithConstructorArgument(kernel.Get<IChartBuilder>("TemperatureChart")); 

Es algo como esto sea posible ?

Respuesta

8

Sugeriría utilizar enlaces contextuales (específicamente nombrados enlaces) para lograr esto. De esa manera usted puede hacer algo como:

// called on app init 
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>().Named("TempChartBuilder"); 
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder().Named("ThreshChartBuilder"); 

// method X/Y could both call method Z that grabs the correct chart director 
var director = new ChartDirector(kernel.Get<IChartBuilder>("TempChartBuilder")); 

Donde "TempChartBuilder" podría ser una variable que indica ninject la que la unión de resolver. Así que más bien vinculante sobre la marcha, se resolvería sobre la marcha, pero todo el enlace podría definirse por adelantado. Normalmente, los contenedores de IOC se almacenan en el nivel de dominio de la aplicación y solo necesitan definirse una vez. Puede haber casos específicos en los que deba vincularse dinámicamente, pero esos deben ser raros.

Más información sobre fijaciones contextuales: https://github.com/ninject/ninject/wiki/Contextual-Binding

+0

+1 como muy útil. Una cosa que no mencioné en mi pregunta original fue que ChartBuilder tiene otras dependencias especificadas durante la inicialización de la aplicación, por lo tanto ninject inyecta automáticamente las otras dependencias requeridas cuando llamo kernel.Get . Entonces, ¿hay alguna manera de hacer lo que sugeriste con 'kernel.Get ' e inyectar el chartbuilder correcto para cada Get específico? – Seth

+0

@Seth: puede considerar crear dos módulos de ninject diferentes que contengan solo los enlaces específicos que desea resolver: https://github.com/ninject/ninject/wiki/Modules-and-the-Kernel. También echaré un vistazo al modelo dinámico de fábrica/proveedor que proporciona ninject: https: // github.com/ninject/ninject/wiki/Proveedores, -Fáctory-Methods-and-the-Activation-Context –

+0

puede ver mi edición - No estoy seguro de si sus sugerencias en su último comentario abordan el último componente de mi pregunta. . Aprecio tu ayuda hasta el momento :) – Seth

15

Si estás planeando usar la ubicación del servicio, como en sus ejemplos, los enlaces a continuación, nombrados funciona bien, según respuesta Garys.

Un mejor enfoque, sin embargo, es usar inyección de constructor y usar atributos. Por ejempl, desde el wiki ninject:

Bind<IWeapon>().To<Shuriken>().Named("Strong"); 
Bind<IWeapon>().To<Dagger>().Named("Weak"); 

...

class WeakAttack { 
    readonly IWeapon _weapon; 
    public([Named("Weak")] IWeapon weakWeapon) 
     _weapon = weakWeapon; 
    } 
    public void Attack(string victim){ 
     Console.WriteLine(_weapon.Hit(victim)); 
    } 
} 

Basado en su comentario a Gary, eres (curiosamente) tropezar en un territorio similar a lo que he hecho una pregunta acerca hace unas horas. Vea la respuesta de Remo aquí: Using WithConstructorArgument and creating bound type

Utilizaría la condición Cuándo para definir cuándo crear la instancia correcta.

Cuestiones relacionadas