使用StructureMap实现策略模式的最佳方式

Jer*_*eir 8 .net structuremap design-patterns dependency-injection

我的Web应用程序在业务逻辑和表示逻辑上有一些细微的变化,具体取决于登录用户的类型.看起来通过根据用户类型注入不同的具体类来获得变化非常适合DI.所以我想知道我应该使用StructureMap的哪些功能来实现这一目标(或者如果我基于DI的目的而离开).

(我刚刚了解到配置文件不是实现此目的的方法,因为设置配置文件不是每线程操作:StructureMap配置文件线程安全吗?)

编辑

这是怎么回事?

public class HomeController
{
    private ISomeDependancy _someDependancy;

    public HomeController(ISomeDependancy someDependancy)
    {
        _someDependancy = someDependancy;
    }

    public string GetNameFromDependancy()
    {
        return _someDependancy.GetName();
    }
}

public interface ISomeDependancy
{
    string GetName();
}

public class VersionASomeDependancy : ISomeDependancy
{
    public string GetName()
    {
        return "My Name is Version A";
    }
}

public class VersionBSomeDependancy : ISomeDependancy
{
    public string GetName()
    {
        return "My Name is Version B";
    }
}

public class VersionARegistry : Registry
{
    public VersionARegistry()
    {
        // build up complex graph here
        ForRequestedType<ISomeDependancy>().TheDefaultIsConcreteType<VersionASomeDependancy>();
    }
}

public class VersionBRegistry : Registry
{
    public VersionBRegistry()
    {
        // build up complex graph here
        ForRequestedType<ISomeDependancy>().TheDefaultIsConcreteType<VersionBSomeDependancy>();
    }
}

public class ContainerA : Container
{
    public ContainerA()
        : base(new VersionARegistry())
    {
    }
}

public class ContainerB : Container
{
    public ContainerB()
        : base(new VersionBRegistry())
    {
    }
}

[TestFixture]
public class Harness
{
    [Test]
    public void ensure_that_versions_load_based_on_named_containers()
    {
        ObjectFactory.Initialize(c =>
        {
            c.ForRequestedType<IContainer>().AddInstances(
                x =>
                {
                    x.OfConcreteType<ContainerA>().WithName("VersionA");
                    x.OfConcreteType<ContainerB>().WithName("VersionB");
                }).CacheBy(InstanceScope.Singleton);
        });

        HomeController controller;

        IContainer containerForVersionA = ObjectFactory.GetNamedInstance<IContainer>("VersionA");
        controller = containerForVersionA.GetInstance<HomeController>();
        Assert.That(controller.GetNameFromDependancy(), Is.EqualTo("My Name is Version A"));

        IContainer containerForVersionB = ObjectFactory.GetNamedInstance<IContainer>("VersionB");
        controller = containerForVersionB.GetInstance<HomeController>();
        Assert.That(controller.GetNameFromDependancy(), Is.EqualTo("My Name is Version B"));
    }
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*gan 11

实现这一点的一种常见方式是Mark所描述的.你有一个类,它接受所有具体实例的数组(它必须是一个数组,以使StructureMap按预期运行),然后使用一些逻辑来确定要使用的实例.

您可以将一些示例代码粘贴到控制台程序或单元测试中:

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
Run Code Online (Sandbox Code Playgroud)

这取决于以下类型:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,您可以创建命名实例,这是解决此问题的另一种常见方法.从容器中检索实例时,您提供实例的名称(而不是配置文件).我建议在你的类中注入一个IContainer并在其上调用GetInstance <>,而不是直接调用ObjectFactory.使用命名实例,您可以为该实例配置整个图. (2认同)

Mar*_*ann 8

我会说这不是DI的核心目的 - 即连接和注入依赖关系,无论它们是什么.组件的接线不应涉及应用逻辑 - 它应严格基于配置; 通过代码或.config文件.该配置是应用程序范围的,因此定义因用户而异的配置非常困难.

也就是说,你所询问的与DI同时进行 - 它本身与DI有点垂直.

出于特定目的,我将以接口或抽象基类的形式定义新的依赖项.这将是一个策略,根据当前用户选择正确的具体类型(您想要改变的类型).

您可以使用DI将所有可用的具体类型注入到此策略中,然后根据当前用户,该策略将具有在这些注入的服务中返回正确选择的方法或属性.

在您过去依赖于各种用户服务的所有地方,您将删除这些旧的依赖项,并将其替换为对策略的依赖性.