views:

538

answers:

2

I am finally getting my feet wet with Dependency Injection (long overdue); I got started playing with Unity and run into an issue with the strategy pattern. I can use the container to return to me specific implementations of a strategy based on a name, but what I don't see is how I am supposed to get the right strategy in the context.
Let's illustrate on a simple example: the context is a car, which has an IEngine (the strategy), with 2 implementations, FastEngine and SlowEngine. The code would look along these lines:

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

My problem is the following: how should I go about instantiating a fast car or a slow car? I can use the container to provide me with each implementation, and I can set a "default" implementation to use:

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

but what I would like is to be able to request a car with a specific implementation of the strategy - something like

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

Can I use the container to do that? Or should I write a Factory which uses the container? Any guidance would be appreciated - I am not sure I am thinking right about this!

A: 

See here: http://stackoverflow.com/questions/787001/can-i-pass-constructor-parameters-to-unitys-resolve-method

(I'm not sure if it could be considered duplicate question)

Konamiman
+6  A: 

A common pattern in DI is that at run-time there's only going to be a single implementation of a given abstraction. That just makes life a whole lot easier, as you don't need to deal with the ambiguity such as the one you describe.

However, sometimes, you need to vary an implementation based on context, such as the example you give. Many DI Containers provide ways where you can provide a qualifying parameter, but that means that you will end up tightly coupling your code to a specific DI Container.

A much better solution would be to introduct an Abstract Factory that can provide what you need. Something like

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

If you need to inject more Strategies, perhaps the Builder design pattern might fit even better.

In any case, the point is that instead of registering a lot of different Cars in the container, you would instead register a single ICarFactory implementation.

In your client code, you would use the injected ICarFactory to create a Car instance based on a particular IEngine.

var car = factory.Create(engine);
Mark Seemann
Thank you, insightful answer. I use the strategy pattern, with multiple strategies swapped at run-time, a lot; by default I would do what you describe (Factory or Builder), but I saw strategy pattern and DI associated a lot, and though this could help. From what you say, it seems that a container would be only marginally helpful.
Mathias
I still think containers are supremely helpful. In these cases, they would just inject the factory instead of the strategy, but I guess that you could still choose to implement the factory with the container...
Mark Seemann
Oh I think I see what you mean; rather than return the right car, return the right factory based on the engine. In any case, your comment re: containers used to provide a single implementation for an abstraction was very helpful; it's consistent with the examples I saw, which are configuration oriented. In that frame, you can have a strategy pattern, but a particular deployment will have only one implementation configured.
Mathias