- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在从事一个业余项目,以更好地理解控制反转和依赖注入(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/
我正在尝试测试依赖于其他服务 authService 的服务 documentViewer angular .module('someModule') .service('docu
如果我的网站上线(不要认为它会,目前它只是一个学习练习)。 我一直在使用 mysql_real_escape_string();来自 POST、SERVER 和 GET 的数据。另外,我一直在使用 i
我有以下代码,它容易受到 SQL 注入(inject)的攻击(我认为?): $IDquery = mysqli_query($connection, "SELECT `ID` FROM users W
我一直在自学如何创建扩展,以期将它们用于 CSS 注入(inject)(以及最终以 CSS 为载体的 SVG 注入(inject),但那是以后的问题)。 这是我当前的代码: list .json {
这个简单的代码应该通过 Java Spring 实现一个简单的工厂。然而结果是空指针,因为 Human 对象没有被注入(inject)对象(所以它保持空)。 我做错了什么? 谢谢 配置 @Config
我正在编写一个 ASP.NET MVC4 应用程序,它最终会动态构建一个 SQL SELECT 语句,以便稍后存储和执行。动态 SQL 的结构由用户配置以用户友好的方式确定,具有标准复选框、下拉列表和
首先让我说我是我为确保 SQL 注入(inject)攻击失败而采取的措施的知己。所有 SQL 查询值都是通过事件记录准备语句完成的,所有运算符(如果不是硬编码)都是通过数字白名单系统完成的。这意味着如
这是 SQL 映射声称可注入(inject)的负载: user=-5305' UNION ALL SELECT NULL,CONCAT(0x716b6b7071,0x4f5577454f76734
我正在使用 Kotlin 和 Android 架构组件(ViewModel、LiveData)构建一个新的 Android 应用程序的架构,并且我还使用 Koin 作为我的依赖注入(inject)提供
假设 RequestScope 处于 Activity 状态(使用 cdi-unit 的 @InRequestScope) 给定 package at.joma.stackoverflow.cdi;
我有一个搜索表单,可以在不同的提供商中搜索。 我从拥有一个基本 Controller 开始 public SearchController : Controller { protected r
SQLite 注入 如果您的站点允许用户通过网页输入,并将输入内容插入到 SQLite 数据库中,这个时候您就面临着一个被称为 SQL 注入的安全问题。本章节将向您讲解如何防止这种情况的发生,确保脚
我可以从什么 dll 中获得 Intercept 的扩展?我从 http://github.com/danielmarbach/ninject.extensions.interception 添加了
使用 NInject 解析具有多个构造函数的类似乎不起作用。 public class Class1 : IClass { public Class1(int param) {...} public
我有一个 MetaManager 类: @Injectable() export class MetaManager{ constructor(private handlers:Handler
我是 Angular 的新手,我不太清楚依赖注入(inject)是如何工作的。我的问题是我有依赖于服务 B 的服务 A,但是当我将服务 A 注入(inject)我的测试服务 B 时,服务 B 变得未定
我正在为我的项目使用 android 应用程序启动、刀柄和空间。我在尝试排队工作时遇到错误: com.test E/WM-WorkerFactory: Could not instantiate co
我不确定这是什么糖语法,但让我向您展示问题所在。 def factors num (1..num).select {|n| num % n == 0} end def mutual_factors
简单的问题,我已经看过这个了:Managing imports in Scalaz7 ,但我不知道如何最小化注入(inject) right和 left方法到我的对象中以构造 \/ 的实例. 我确实尝
在我的 Aurelia SPA 中,我有一些我想在不同模块中使用的功能。它依赖于调用时给出的参数和单例的参数。有没有办法创建一个导出函数,我可以将我的 Auth 单例注入(inject)其中,而不必在
我是一名优秀的程序员,十分优秀!