Шаблон стратегии и внедрение зависимостей с использованием Unity - PullRequest
24 голосов
/ 10 ноября 2009

Я наконец-то намочил ноги с помощью инъекций зависимостей (давно пора); Я начал играть с Unity и столкнулся с проблемой со схемой стратегии. Я могу использовать контейнер, чтобы вернуть мне конкретные реализации стратегии, основанной на имени, но я не вижу, как я должен получить правильную стратегию в контексте.
Давайте проиллюстрируем на простом примере: контекст - это машина, у которой есть IEngine (стратегия) с двумя реализациями, FastEngine и SlowEngine. Код будет выглядеть следующим образом:

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

Моя проблема заключается в следующем: как мне создать экземпляр быстрой или медленной машины? Я могу использовать контейнер, чтобы предоставить мне каждую реализацию, и я могу установить реализацию по умолчанию:

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

но я бы хотел запросить автомобиль с конкретной реализацией стратегии - что-то вроде

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

Могу ли я использовать контейнер для этого? Или я должен написать Фабрику, которая использует контейнер? Будем благодарны за любые указания - я не уверен, что правильно об этом думаю!

1 Ответ

27 голосов
/ 10 ноября 2009

Общий шаблон в DI состоит в том, что во время выполнения будет только одна реализация данной абстракции. Это просто делает жизнь намного проще, так как вам не нужно иметь дело с такой неопределенностью, как та, которую вы описываете.

Однако иногда вам нужно варьировать реализацию в зависимости от контекста, такого как пример, который вы приводите. Многие DI-контейнеры предоставляют способы предоставления квалифицирующего параметра, но это означает, что в конечном итоге вы будете тесно связывать свой код с конкретным DI-контейнером.

Гораздо лучшим решением было бы введение Abstract Factory , которая может предоставить то, что вам нужно. Что-то вроде

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

Если вам нужно добавить больше стратегий, возможно, шаблон проектирования Builder подойдет еще лучше.

В любом случае, смысл в том, что вместо регистрации большого количества разных автомобилей в контейнере вы должны зарегистрировать одну реализацию ICarFactory.

В вашем клиентском коде вы использовали бы введенный ICarFactory для создания экземпляра Car на основе определенного IEngine.

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