23

Finalmente me estoy mojando los pies con Inyección de Dependencia (hace mucho tiempo); Comencé a jugar con Unity y me encontré con un problema con el patrón de estrategia. Puedo utilizar el contenedor para devolverme implementaciones específicas de una estrategia basada en un nombre, pero lo que no veo es cómo se supone que debo obtener la estrategia correcta en el contexto.
Vamos a ilustrar un ejemplo simple: el contexto es un automóvil, que tiene un IEngine (la estrategia), con 2 implementaciones, FastEngine y SlowEngine. El código se vería a lo largo de estas líneas:Patrón de estrategia e inyección de dependencia usando Unity

public interface IEngine 
{ 
    double MaxSpeed 
    { 
     get; 
    } 
} 

internal class FastEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 100d; 
     } 
    } 
} 

internal class SlowEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 10d; 
     } 
    } 
} 

public class Car 
{ 
    private IEngine engine; 
    public double MaximumSpeed 
    { 
     get 
     { 
      return this.engine.MaxSpeed; 
     } 
    } 

    public Car(IEngine engine) 
    { 
     this.engine = engine; 
    } 
} 

Mi problema es el siguiente: ¿cómo debo ir sobre crear instancias de un coche rápido o un coche lento? Puedo utilizar el recipiente para proveerme con cada aplicación, y puedo fijar una aplicación "default" para utilizar:

IUnityContainer container = new UnityContainer(); 
container.RegisterType<IEngine, FastEngine>(); 
container.RegisterType<IEngine, FastEngine>("Fast"); 
container.RegisterType<IEngine, SlowEngine>("Slow"); 
var car = container.Resolve<Car>(); 
Assert.AreEqual(100, car.MaximumSpeed); 

pero lo que me gustaría es ser capaz de pedir un coche con una aplicación específica de la estrategia - algo así como

var car = container.Resolve<Car>(??? use "Fast" or "Slow ???); 

¿Puedo usar el contenedor para hacer eso? ¿O debería escribir una fábrica que usa el contenedor? Cualquier orientación sería apreciada. ¡No estoy seguro de estar pensando en esto!

Respuesta

26

Un patrón común en DI es que en tiempo de ejecución solo va a haber una implementación única de una abstracción determinada. Eso simplemente hace la vida mucho más fácil, ya que no necesita lidiar con la ambigüedad como la que describe.

Sin embargo, a veces, debe variar una implementación en función del contexto, como el ejemplo que proporciona. Muchos Contenedores DI proporcionan formas en las que puede proporcionar un parámetro calificativo, pero eso significa que terminará acoplando estrechamente su código a un Contenedor DI específico.

Una solución mucho mejor sería introducir un Abstract Factory que pueda proporcionarle lo que necesita. Algo así como

public interface ICarFactory 
{ 
    Car Create(IEngine engine); 
} 

Si necesita inyectar más estrategias, tal vez el patrón de diseño Builder podría encajar mejor.

En cualquier caso, el punto es que en lugar de registrar una gran cantidad de automóviles diferentes en el contenedor, en su lugar, registraría una única implementación de ICarFactory.

En su código de cliente, usaría ICarFactory inyectado para crear una instancia de Car basada en un IE Engine particular.

var car = factory.Create(engine); 
+0

Gracias, respuesta perspicaz. Uso el patrón de estrategia, con múltiples estrategias intercambiadas en tiempo de ejecución, mucho; de forma predeterminada, haría lo que describes (Factory o Builder), pero vi un patrón de estrategia y DI asociados mucho, y aunque esto podría ser útil. Por lo que dices, parece que un contenedor solo sería marginalmente útil. – Mathias

+0

Sigo pensando que los contenedores son sumamente útiles. En estos casos, simplemente inyectarían la fábrica en lugar de la estrategia, pero supongo que aún podrías elegir implementar la fábrica con el contenedor ... –

+4

Oh, creo que entiendo a qué te refieres; en lugar de devolver el automóvil correcto, devuelva la fábrica correcta en función del motor. En cualquier caso, su comentario sobre los contenedores utilizados para proporcionar una única implementación para una abstracción fue muy útil; es consistente con los ejemplos que vi, que están orientados a la configuración. En ese marco, puede tener un patrón de estrategia, pero una implementación en particular solo tendrá configurada una implementación. – Mathias

Cuestiones relacionadas