Dot-Net
使用 StructureMap 實現策略模式的最佳方法
根據登錄的使用者類型,我的 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; } }