Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StructureMap: Choose concrete type of nested dependency

Calculators:

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

Calculator stragies:

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

Calculator usages:

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

Structuremap Registry:

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

For CalculatorUsageOne I want to add the numbers (use AdditionCalculator). For CalculatorUsageTwo I want to multiply the numbers (use MultiplyCalculator).

How do I achieve this with StructureMap?

like image 458
Rookian Avatar asked Jul 04 '11 20:07

Rookian


2 Answers

Try like this:

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

You can nest your objects graph configurations as deep as you need. Anyway, I would think about using generics here to show the dependencies in more explicit way.

edit about generics:

Whether using generics is a good idea here depends from your scenario. If you didn't specified the concrete dependencies for your CalculatorUsages on purpose and that's your goal to have it strategy-agnostic, your solution seems to be the best.

But if you just need to have common implementation of Calculator in "middle layer", you can specify Calculator's dependency in generic parameter to make it explicit. Maybe it's not the best use case here, but it can go somehow like this:

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

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

and register it like this:

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

This will tell StructureMap to pass any generic parameter for requested ICalculator to Calculator (open generics), which then instantiates the strategy objects in constructor.

Alternatively, you can use marker interfaces instead of generics, but once again, it all depends from your particular scenario and it may be that the simplest solution from very beginning fits best.

like image 183
NOtherDev Avatar answered Nov 12 '22 21:11

NOtherDev


One method is to define the dependency when getting an instance using the With method.

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

The only other way I can think of is providing the isntances as constructor arguments when registering the types. I can provide an example tomorrow.

like image 1
Zebi Avatar answered Nov 12 '22 20:11

Zebi