Best way to use StructureMap to implement Strategy pattern

。_饼干妹妹 提交于 2019-11-26 17:47:05

One common way to implement this is as Mark described. You have a class that takes in an array of all concrete instances (it must be an array for StructureMap to behave as expected), and then uses some logic to figure out which instance to use.

Some sample code you can paste into a console program or unit test:

var container = new Container(x => x.Scan(scan =>
{
    scan.TheCallingAssembly();
    scan.WithDefaultConventions();
    scan.AddAllTypesOf<IDiscountCalculator>();
}));
var strategy = container.GetInstance<IDiscountStrategy>();
Console.WriteLine(strategy.GetDiscount("Regular", 10)); // 0
Console.WriteLine(strategy.GetDiscount("Normal", 10)); // 1
Console.WriteLine(strategy.GetDiscount("Special", 10)); // 5

which depends on the following types:

public interface IDiscountStrategy 
{
    decimal GetDiscount(string userType, decimal orderTotal);
}

public class DiscountStrategy : IDiscountStrategy
{
    private readonly IDiscountCalculator[] _discountCalculators;

    public DiscountStrategy(IDiscountCalculator[] discountCalculators)
    {
        _discountCalculators = discountCalculators;
    }

    public decimal GetDiscount(string userType, decimal orderTotal)
    {
        var calculator = _discountCalculators.FirstOrDefault(x => x.AppliesTo(userType));
        if (calculator == null) return 0;
        return calculator.CalculateDiscount(orderTotal);
    }
}

public interface IDiscountCalculator
{
    bool AppliesTo(string userType);
    decimal CalculateDiscount(decimal orderTotal);
}

public class NormalUserDiscountCalculator : IDiscountCalculator
{
    public bool AppliesTo(string userType)
    {
        return userType == "Normal";
    }

    public decimal CalculateDiscount(decimal orderTotal)
    {
        return orderTotal * 0.1m;
    }
}

public class SpecialUserDiscountCalculator : IDiscountCalculator
{
    public bool AppliesTo(string userType)
    {
        return userType == "Special";
    }

    public decimal CalculateDiscount(decimal orderTotal)
    {
        return orderTotal * 0.5m;
    }
}

I would say that this is not the core purpose of DI - that is to wire up and inject dependencies, whatever they might be. No application logic should be involved in the wiring up of components - it should be based stricly on configuration; either by code or .config file. That configuration is application-wide, so it's quite difficult to define a configuration that varies by user.

That said, what you are asking about goes well hand in hand with DI - it's just a bit perpendicular to DI in itself.

For your specific purpose, I would define a new dependency in the form of an interface or abstract base class. This would be a Strategy that selects the correct concrete types (the ones you want to vary) based on the current user.

You can use DI to inject all the available concrete types into this Strategy, which would then have a method or property that returns the correct choice among those injected services, based on the current user.

In all the places where you used to depend on the vary-by-user-services, you remove those old dependencies and replace them with a dependency on the Strategy.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!