StructureMap: выберите конкретный тип вложенной зависимости

Калькуляторы:

public interface ICalculator
{
    int Calculate(int a, int b);
}

public class Calculator : ICalculator
{
    private readonly ICalculatorStrategy _calculatorStrategy;

    public Calculator(ICalculatorStrategy calculatorStrategy)
    {
        _calculatorStrategy = calculatorStrategy;
    }

    public int Calculate(int a, int b)
    {
        return _calculatorStrategy.Calculate(a, b);
    }
}

Калькулятор страги:

public interface ICalculatorStrategy
{
    int Calculate(int a, int b);
}

public class AdditionCalculator : ICalculatorStrategy
{
    public int Calculate(int a, int b)
    {
        return a + b;
    }
}

public class MultiplyCalculator : ICalculatorStrategy
{
    public int Calculate(int a, int b)
    {
        return a * b;
    }
}

Использование калькулятора:

public class CalculatorUsageOne
{
    private readonly ICalculator _calculator;

    public CalculatorUsageOne(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public void Process()
    {
        Console.WriteLine(_calculator.Calculate(6, 5));
    }
}

public class CalculatorUsageTwo
{
    private readonly ICalculator _calculator;

    public CalculatorUsageTwo(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public void Process()
    {
        Console.WriteLine(_calculator.Calculate(6, 5));
    }
}

Структура карты реестра:

public class DependencyRegistry : Registry
{
    public DependencyRegistry()
    {
        For<ICalculatorStrategy>().Use<AdditionCalculator>().Named("Addition");
        For<ICalculatorStrategy>().Use<MultiplyCalculator>().Named("Multiply");
        For<ICalculator>().Use<Calculator.Calculator>();
    }
}

Для CalculatorUsageOne я хочу добавить цифры (используйте AdditionCalculator). Для CalculatorUsageTwo я хочу умножить числа (используйте MultiplyCalculator).

Как мне добиться этого с StructureMap?

2 ответа

Решение

Попробуйте вот так:

For<CalculatorUsageOne>().Use<CalculatorUsageOne>()
    .Ctor<ICalculator>().Is<Calculator.Calculator>(
        x => x.Ctor<ICalculatorStrategy>().Is<AdditionCalculator>()
    );
For<CalculatorUsageTwo>().Use<CalculatorUsageTwo>()
    .Ctor<ICalculator>().Is<Calculator.Calculator>(
        x => x.Ctor<ICalculatorStrategy>().Is<MultiplyCalculator>()
    );

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

редактировать о дженериках:

Является ли использование дженериков хорошей идеей, зависит от вашего сценария. Если вы не указали конкретные зависимости для вашего CalculatorUsages намеренно, и это ваша цель, чтобы не зависеть от стратегии, ваше решение кажется лучшим.

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

public class CalculatorUsageOne
{
    public CalculatorUsageOne(ICalculator<AdditionCalculator> calculator)
    {
        // ...
    }
}

public class Calculator<T> where T : ICalculatorStrategy
{
    public Calculator(T strategy)
    {
        // ...
    }
}

и зарегистрируйте это так:

For(typeof(ICalculator<>).Use(typeof(Calculator<>);

Это скажет StructureMap передать любой универсальный параметр для запрошенного ICalculator в Calculator ( open generics), который затем создает объекты стратегии в конструкторе.

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

Одним из методов является определение зависимости при получении экземпляра с использованием With метод.

var additionStrategy = ObjectFactory
                            .GetNamedInstance<ICalculatorStrategy>("Addition");
var c1 = ObjectFactory.With(additionStrategy).GetInstance<CalculatorUsageOne>();

Единственный другой способ, который я могу придумать, - предоставить isntances в качестве аргументов конструктора при регистрации типов. Я могу привести пример завтра.

Другие вопросы по тегам