【问题标题】:Best way to use StructureMap to implement Strategy pattern使用 StructureMap 实现策略模式的最佳方法
【发布时间】:2009-09-30 17:03:58
【问题描述】:

根据登录用户的类型,我的 Web 应用在业务逻辑和表示逻辑上有一些细微的变化。通过基于用户类型注入不同的具体类来获得变化似乎非常适合 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"));
    }
}

【问题讨论】:

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


    【解决方案1】:

    实现这一点的一种常见方法是如 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;
        }
    }
    

    【讨论】:

    • 通过将AppliesTo() 方法放在那里,这不是将其外部使用的知识嵌入到每个DiscountCalculator 中吗?出于某种原因,我认为可能有类似于 Profiles 功能的东西可以用来消除该 Strategy 类。你可以在哪里做 ObjectFactory.GetInstance("NameOfProfile")
    • 另外,如果 DiscountCalculators 有一些自己的依赖项,但也会根据用户类型而有所不同,该怎么办?您如何获得一个完整的图表来加载任何可能在 userType 上发生变化的具体图表?
    • 是的,您可以创建命名实例,这是解决此问题的另一种常用方法。从容器中检索实例时,您提供实例的名称(不是配置文件)。我建议在你的类中注入一个 IContainer 并在其上调用 GetInstance,而不是直接调用 ObjectFactory。使用命名实例,您可以为该实例配置整个图表。
    • 感谢 Joshua,我是否正确实施了您在编辑中描述的内容?
    • 这不是我的想法,但我想如果这是您需要的(2 个完全不同的配置容器),它应该可以工作。我的意思是在你的类中找到命名服务: continer.GetInstance("VersionB") 其中容器是构造函数中的 IContainer 注入(而不是直接在你的类中调用 ObjectFactory)。
    【解决方案2】:

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

    也就是说,您所问的问题与 DI 密切相关 - 它只是与 DI 本身有点垂直。

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

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

    在您过去依赖于用户不同服务的所有地方,您删除了那些旧的依赖项并将它们替换为对策略的依赖项。

    【讨论】:

    • +1 用于给出我正在尝试做的正确名称(我更改了问题的标题以反映)。但你基本上只是重申了我想要完成的事情。我想知道 StructureMap 的哪些功能最适合实现“策略”模式。或者,如果 DI 不打算做这样的事情。
    • DI 没有实现策略模式——你做到了。您必须编写策略实现以及使用它的代码。一个 DI Container 可以而且应该做的就是确保这两者相遇。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多