Design patterns 正确使用IoC容器如何帮助您避免使用工厂?

Design patterns 正确使用IoC容器如何帮助您避免使用工厂?,design-patterns,dependency-injection,inversion-of-control,ioc-container,simple-injector,Design Patterns,Dependency Injection,Inversion Of Control,Ioc Container,Simple Injector,许多IoC容器都具有“自动工厂”的功能,它根据接口生成抽象工厂的实现。然而,它通常是一个二等公民:对于这个特性,而Simple Injector的作者故意从库中省略了auto Factorys,声称这一点。这不是我遇到类似观点的唯一地方 然而,在我看来,在使用IoC和编写领域模型时,工厂似乎是设计中不可或缺的元素。比方说,一个简单的虚构示例可以是: class Order { private ITaxProvider _taxProvider; public decimal Q

许多IoC容器都具有“自动工厂”的功能,它根据接口生成抽象工厂的实现。然而,它通常是一个二等公民:对于这个特性,而Simple Injector的作者故意从库中省略了auto Factorys,声称这一点。这不是我遇到类似观点的唯一地方

然而,在我看来,在使用IoC和编写领域模型时,工厂似乎是设计中不可或缺的元素。比方说,一个简单的虚构示例可以是:

class Order
{
    private ITaxProvider _taxProvider;

    public decimal Quantity { get; set; }
    public decimal PricePerUnit { get; set; }
    public decimal Cost { get; set; }

    public Order(ITaxProvider taxProvider)
    {
        _taxProvider = taxProvider;
    }

    public decimal GetProfit()
    {
        // Don't nitpick on this example, please, it's just to show some
        // kind of domain object that depends on some kind of service,
        // but also has some logic of its own.
        decimal grossProfit = Quantity * PricePerUnit - Cost;
        decimal netProfit = grossProfit * (1.0m - _taxProvider.GetTaxRate());
        return netProfit;
    }
}
在这种情况下,每次我在MVC控制器中创建一个
订单
,我都需要使用工厂。最终,对于任何域对象,这很可能都是正确的。即使它们现在可能没有任何依赖项,也很可能有人在某个时候会根据配置值请求某些功能以不同的方式运行,或者某些代码将被重构为单独的类,然后可以使用DI将这些类解耦,以进行分离测试


所以我几乎愿意推出工厂来控制每个域对象的初始化。但是比我更有经验的开发人员说,我不需要经常使用带有IoC容器的工厂。在我看来,域模型永远不应该包含依赖项。它们应该尽可能靠近a。在我的系统中只有一些消息,可以使用方法注入轻松地从一个类传送到另一个类

正如前面的评论所建议的那样,对这些模式进行了描述和分析

如果我们将
订单
作为POCO,我们将如何计算利润?通过提供只计算利润的服务:

公共类ProfitCalculator
{
私有只读ITaxProvider taxProvider;
公共利润计算器(ITaxProvider taxProvider)
{
this.taxProvider=taxProvider;
}
公共利益(订单)
{
decimal grossProfit=订单.数量*订单.价格单位-订单.成本;
十进制净利润=grossProfit*(1.0m-this.taxProvider.GetTaxRate());
返还净利润;
}
}
假设
ITaxProvider
可以是单例,
ProfitCalculator
也可以是单例。秩序只是一个简单的对象:

类顺序
{
公共十进制数量{get;set;}
公共十进制价格单位{get;set;}
公共十进制成本{get;set;}
}
这将完全消除建立工厂的需要。使用引用的blogpost中描述的基于消息的模式将使您的生活更加轻松

更新:

正如史蒂文所说,这不是唯一的解决办法。您还可以定义一个将依赖项作为参数的方法,而不是创建计算器类。我更喜欢将这种方法实现为如下的扩展方法:

公共静态类域扩展
{
公共静态十进制GetProfit(此订单,ITaxProvider taxProvider)
{
decimal grossProfit=订单.数量*订单.价格单位-订单.成本;
十进制净利润=grossProfit*(1.0m-taxProvider.GetTaxRate());
返还净利润;
}
}

要理解Steven将工厂排除在Simple Injector之外的原因,您应该从一开始就开始:该论点反对返回其他组件的工厂,而不是反对任何类型的工厂,例如返回域对象或其他值类型的工厂。返回组件的工厂尤其是容器的“自动工厂”功能提供的工厂类型。那么您的税务提供者是这里的工厂吗?从这个意义上说,存储库也是工厂。它们没有问题。@Steven没有,我没有显示工厂,工厂将用于创建
Order
的实例
ITaxProvider
是一个依赖项,将由IoC容器提供。这就是为什么需要工厂的原因。允许创建实体的抽象不是工厂;这是一个存储库。但是,您所犯的错误是在实体构造函数中插入组件。这被认为是不好的。您的IoC容器不应承担创建实体的任务;它的工作是构造组件。域对象依赖于组件是可以的,但是不应该将它们注入构造函数中,这些依赖项应该简单地作为方法参数传递,如:
GetProfit(ITaxProvider provider)
。但是,当使用建议的模式时,真正的域对象将是您的命令对象。