我了解DI背后的概念,但我只是在学习不同的IoC容器可以做什么。似乎大多数人主张使用IoC容器来连接无状态服务,但是将它们用于诸如实体之类的有状态对象又如何呢?

无论是对还是错,我通常会用行为来填充我的实体,即使这种行为需要一个外部类。示例:

public class Order : IOrder
{

    private string _ShipAddress;
    private IShipQuoter _ShipQuoter;

    public Order(IOrderData OrderData, IShipQuoter ShipQuoter)
    {
        // OrderData comes from a repository and has the data needed 
        // to construct order
        _ShipAddress = OrderData.ShipAddress;  // etc.
        _ShipQuoter = ShipQuoter;

    }

    private decimal GetShippingRate()
    {
        return _ShipQuoter.GetRate(this);
    }
}


如您所见,依赖项是构造函数注入的。现在有几个问题。


让您的实体依赖于ShipQuoter等外部类是否被认为是不好的做法?如果我正确理解了定义,消除这些依赖关系似乎会使我走向贫血领域。
使用IoC容器解析这些依赖关系并在需要时构造实体是否是一种不好的做法?可以这样做吗?

感谢您的见解。

评论

只是做您需要做的事情,因为它使您的工作更轻松,而不是因为这可能是您应该这样做的方式

作为一个自学成才的程序员,我走了那条路,并导致了我公司当前使用的软件。出现了一个基于过程/事务脚本的软件,部分原因是它最简单易行,因为我不了解。维护和扩展绝对是痛苦的,这就是为什么我要花时间重新编写它,并向已经克服这些问题的人寻求建议,以防止他们犯同样的错误。

相关:stackoverflow.com/questions/827670/…

#1 楼

第一个问题是最难回答的。让实体依赖外部类是不好的做法吗?当然,这不是最常见的事情。

例如,如果将存储库注入到实体中,则可以有效地实现Active Record模式。有些人喜欢此模式以提供便利,而其他人(如我)则认为它是代码气味或反模式,因为它违反了单一职责原则(SRP)。

您可以争辩说注入其他进入实体的依赖关系将使您朝同一方向发展(远离SRP)。另一方面,如果您不这样做,您肯定是对的,这是朝着贫血领域模型的方向发展的。

我花了很长的时间为此苦苦挣扎,直到遇到Greg Young的DDDD上的一篇(被遗弃的)论文,他解释了为什么刻板的n层/ n层体系结构将始终是CRUDy(因此很贫乏)。代替名词似乎使我们能够构建适当的面向对象的域模型。

第二个问题更容易回答。您始终可以在运行时使用Abstract Factory创建实例。有了Castle Windsor,您甚至可以使用Typed Factory Facility,从而减轻了手动实施工厂的负担。

评论


谢谢马克。我已经看过Typed Factory,并阅读了有关Abstract Factory方法的其他文章,但是我从未见过将它们用于解析实体的任何示例。这是因为大多数人在设计自己的实体时除了存储库外没有任何依赖关系吗?如果我严格使用“类型化工厂”之类的东西来解决我具有外部依赖项的实体,我会遇到麻烦吗?

–卡西·威尔金斯(Casey Wilkins)
2011年1月29日15:48

我要说的是,如果您的实体包含其他可以访问其他实体的协作者,等等,那么您可能会遇到各种各样的维护问题-更不用说违反SRP和N + 1的问题了。因此,埃文斯(Evans)建议将每个实体视为汇总根。

– Mark Seemann
11年1月29日在18:34

在我的示例中,ShipQuoter从网络服务(例如UPS)中提取订单的运费。您将其设置为接受IOrder的服务还是将其作为域对象(如Order.GetRates)的一部分?

–卡西·威尔金斯(Casey Wilkins)
11年1月29日在21:45

我将花费大量时间弄清楚如何避免一开始就出现同步拉动。您以同步,阻塞的方式提取数据的次数越多,设计就越脆弱。这就是为什么CQRS如此吸引人的原因。

– Mark Seemann
2011年1月30日9:35

您与Greg论文的链接已失效。但这里仍然可用。看起来这是一个较新的版本。

– BornToCode
16年6月14日在12:55

#2 楼

我知道这是旧帖子,但想添加。即使您在ctor中传入了一个抽象存储库,该域实体也不应保留其自身。我建议这样做的原因不仅是它违反了SRP,而且还违背了DDD的聚合。让我解释一下,DDD适合具有固有深图的复杂应用程序,因此,我们使用聚合或复合根来持久化对基础“子级”的更改,因此,当我们将持久性注入单个子级时,就违反了子级与子级之间的关系。复合或聚合根,应“负责”生命周期或聚合。当然,复合根或聚合也不会保留其自己的图。另一个与注入DDD对象的依赖性有关的是,注入的域对象实际上没有状态,直到发生其他事件以使其状态水化为止。代码的任何使用者将被迫首先初始化或设置域对象,然后他们才能调用违反封装的业务行为。