Ashley John
Ashley John

Reputation: 2453

Injecting Corresponding implementation using StructureMap

I have a class with the following structure

class Measure
{
    public MeasureType measureType { get; set; }
    public readonly IMeasureService _service ;

    public Measure(IMeasureService service)
    {
        _service = service;
    }
    public Calculate()
    {
        _service.Calculate(this);
    }
}

So i have different implementation of MeasureService for different measureTypes. Is it possible to use StructureMap to inject the corresponding MeasureService based on the measureType.

MeasureType is just an enum type and will never get changed throughout the instance.

Upvotes: 2

Views: 405

Answers (1)

Cristian Lupascu
Cristian Lupascu

Reputation: 40526

StructureMap cannot be instructed to do that for a simple reason:

  • at the time the instance is created, the measureType property cannot be set.
  • you cannot set the IMeasureService dependency at a later time, because it's readonly (and should probably be private as well)

Therefore, the solutions to this problem have more to do with the design of your classes than with StructureMap.

Here are the solutions I would typically choose in such a scenario:


1) Delegate the task of choosing the right IMeasureService implementation to a different component.

The component could, for example, be called MeasureServiceFactory and have the following implementation:

public class MeasureServiceFactory
{
    private readonly Func<MeasureServiceType1> _type1Service;
    private readonly Func<MeasureServiceType2> _type2Service;

    public MeasureServiceFactory(
        Func<MeasureServiceType1> type1Service,
        Func<MeasureServiceType2> type2Service)
    {
        _type1Service = type1Service;
        _type2Service = type2Service;
    }

    public IMeasureService GetServiceForType(MeasureType type)
    {
        switch (type)
        {
            case MeasureType.Type1:
                return _type1Service();
            case MeasureType.Type2:
                return _type2Service();
            default:
                throw new ApplicationException("Unexpected measure type!");
        }
    }
}

Note that StructureMap will automatically inject the two Funcs with appropriate calls to the container's GetInstance() method in order to properly resolve the required service.

Now, the Measure class will no longer have a direct IMeasureService dependency, but will depend on the factory instead:

public class Measure
{
    public MeasureType measureType { get; set; }
    private readonly MeasureServiceFactory _measureServiceFactory;

    public Measure(MeasureServiceFactory measureServiceFactory)
    {
        _measureServiceFactory = measureServiceFactory;
    }

    public void Calculate()
    {
        var service = _measureServiceFactory.GetServiceForType(measureType);
        service.Calculate(this);
    }
}

Note that no StructureMap configuration is required. The container will be able to resolve everything correctly.


2) In order to keep the Measure class untouched, you could make the MeasureServiceFactory implement the IMeasureService interface:

public class MeasureServiceFactory : IMeasureService
{
    private readonly Func<MeasureServiceType1> _type1Service;
    private readonly Func<MeasureServiceType2> _type2Service;

    public MeasureServiceFactory(
        Func<MeasureServiceType1> type1Service,
        Func<MeasureServiceType2> type2Service)
    {
        _type1Service = type1Service;
        _type2Service = type2Service;
    }

    public void Calculate(Measure measure)
    {
        var service = GetServiceForType(measure.measureType);

        service.Calculate(measure);
    }

    private IMeasureService GetServiceForType(MeasureType type)
    {
        switch (type)
        {
            case MeasureType.Type1:
                return _type1Service();
            case MeasureType.Type2:
                return _type2Service();
            default:
                throw new ApplicationException("Unexpected measure type!");
        }
    }
}

Now, with your original Measure class, only this SM configuration is required to ake everything work:

x.For<IMeasureService>().Use<MeasureServiceFactory>();

3) If the logic inside the IMeasureService implementors is not very complicated, consider putting it in a single class and perform different things depending on the measureType of the Measure object (which you already have access to).

I know, this seems like a step back, but there are some cases in which I prefer to do just that: when the business logic is not overly complex and pretty unlikely to change.

Upvotes: 2

Related Questions