Dot-Net

使用 StructureMap 實現策略模式的最佳方法

  • October 5, 2009

根據登錄的使用者類型,我的 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"));
   }
}

實現這一點的一種常見方法是如 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;
   }
}

引用自:https://stackoverflow.com/questions/1499442