gpt4 book ai didi

c# - 使用具有依赖注入(inject)的策略和工厂模式

转载 作者:太空狗 更新时间:2023-10-29 18:02:16 24 4
gpt4 key购买 nike

我正在从事一个业余项目,以更好地理解控制反转和依赖注入(inject)以及不同的设计模式。

我想知道是否有将 DI 与工厂和策略模式结合使用的最佳实践

当一个策略(从工厂构建)需要为每个可能的构造函数和实现使用不同的参数时,我的挑战就来了。结果,我发现自己在服务入口点声明了所有可能的接口(interface),并通过应用程序向下传递它们。因此,必须为新的和各种策略类实现更改入口点。

为了便于说明,我在下面整理了一个成对的示例。我在这个项目中的堆栈是 .NET 4.5/C# 和 Unity for IoC/DI。

在这个示例应用程序中,我添加了一个默认的 Program 类,它负责接受一个虚构的订单,并根据订单属性和选择的运输提供商计算运输成本。 UPS、DHL 和 Fedex 有不同的计算方式,每个实现可能依赖也可能不依赖其他服务(访问数据库、api 等)。

public class Order
{
public string ShippingMethod { get; set; }
public int OrderTotal { get; set; }
public int OrderWeight { get; set; }
public int OrderZipCode { get; set; }
}

计算运费的虚构程序或服务

public class Program
{
// register the interfaces with DI container in a separate config class (Unity in this case)
private readonly IShippingStrategyFactory _shippingStrategyFactory;

public Program(IShippingStrategyFactory shippingStrategyFactory)
{
_shippingStrategyFactory = shippingStrategyFactory;
}

public int DoTheWork(Order order)
{
// assign properties just as an example
order.ShippingMethod = "Fedex";
order.OrderTotal = 90;
order.OrderWeight = 12;
order.OrderZipCode = 98109;

IShippingStrategy shippingStrategy = _shippingStrategyFactory.GetShippingStrategy(order);
int shippingCost = shippingStrategy.CalculateShippingCost(order);

return shippingCost;
}
}

// Unity DI Setup
public class UnityConfig
{
var container = new UnityContainer();
container.RegisterType<IShippingStrategyFactory, ShippingStrategyFactory>();
// also register IWeightMappingService and IZipCodePriceCalculator with implementations
}

public interface IShippingStrategyFactory
{
IShippingStrategy GetShippingStrategy(Order order);
}

public class ShippingStrategyFactory : IShippingStrategyFactory
{
public IShippingStrategy GetShippingStrategy(Order order)
{
switch (order.ShippingMethod)
{
case "UPS":
return new UPSShippingStrategy();

// The issue is that some strategies require additional parameters for the constructor
// SHould the be resolved at the entry point (the Program class) and passed down?
case "DHL":
return new DHLShippingStrategy();

case "Fedex":
return new FedexShippingStrategy();

default:
throw new NotImplementedException();
}
}
}

现在是 Strategy 接口(interface)和实现。UPS 是一个简单的计算,而 DHL 和 Fedex 可能需要不同的服务(和不同的构造函数参数)。

public interface IShippingStrategy
{
int CalculateShippingCost(Order order);
}

public class UPSShippingStrategy : IShippingStrategy()
{
public int CalculateShippingCost(Order order)
{
if (order.OrderWeight < 5)
return 10; // flat rate of $10 for packages under 5 lbs
else
return 20; // flat rate of $20
}
}

public class DHLShippingStrategy : IShippingStrategy()
{
private readonly IWeightMappingService _weightMappingService;

public DHLShippingStrategy(IWeightMappingService weightMappingService)
{
_weightMappingService = weightMappingService;
}

public int CalculateShippingCost(Order order)
{
// some sort of database call needed to lookup pricing table and weight mappings
return _weightMappingService.DeterminePrice(order);
}
}

public class FedexShippingStrategy : IShippingStrategy()
{
private readonly IZipCodePriceCalculator _zipCodePriceCalculator;

public FedexShippingStrategy(IZipCodePriceCalculator zipCodePriceCalculator)
{
_zipCodePriceCalculator = zipCodePriceCalculator;
}

public int CalculateShippingCost(Order order)
{
// some sort of dynamic pricing based on zipcode
// api call to a Fedex service to return dynamic price
return _zipCodePriceService.CacluateShippingCost(order.OrderZipCode);
}
}

上面的问题是每个策略都需要额外的和不同的服务来执行“CalculateShippingCost”方法。这些接口(interface)/实现是否需要注册到入口点(Program 类)并通过构造函数向下传递?

还有其他模式更适合完成上述场景吗?也许 Unity 可以专门处理某些事情 (https://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx)?

我非常感谢在正确方向上的任何帮助或插入。

谢谢,安迪

最佳答案

有几种方法可以做到这一点,但我更喜欢的方法是将可用策略列表注入(inject)您的工厂,然后过滤它们以返回您感兴趣的策略。

使用您的示例,我将修改 IShippingStrategy 以添加新属性:

public interface IShippingStrategy
{
int CalculateShippingCost(Order order);
string SupportedShippingMethod { get; }
}

然后我会像这样实现工厂:

public class ShippingStrategyFactory : IShippingStrategyFactory
{
private readonly IEnumerable<IShippingStrategy> availableStrategies;

public ShippingStrategyFactory(IEnumerable<IShippingStrategy> availableStrategies)
{
this.availableStrategies = availableStrategies;
}

public IShippingStrategy GetShippingStrategy(Order order)
{
var supportedStrategy = availableStrategies
.FirstOrDefault(x => x.SupportedShippingMethod == order.ShippingMethod);
if (supportedStrategy == null)
{
throw new InvalidOperationException($"No supported strategy found for shipping method '{order.ShippingMethod}'.");
}

return supportedStrategy;
}
}

我喜欢这样使用它的主要原因是我永远不必回来修改工厂。如果我必须实现新策略,则不必更改工厂。如果您在容器中使用自动注册,您甚至不必注册新策略,因此您可以花更多时间编写新代码。

关于c# - 使用具有依赖注入(inject)的策略和工厂模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42402064/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com