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

时间:2009-09-30 17:03:58

标签: .net design-patterns dependency-injection structuremap

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

(我刚刚了解到Profiles不是实现此目的的方法,因为设置Profile不是每个线程的操作:Are StructureMap profiles thread safe?

修改

这是解决这个问题的方法吗?

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

2 个答案:

答案 0 :(得分: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

取决于以下类型:

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

答案 1 :(得分:8)

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

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

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

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

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