控制反转、SRP 依赖注入和延迟加载

发布于 2024-08-17 16:30:46 字数 1983 浏览 7 评论 0原文

我和一位开发人员同事正在(轻松地说)讨论对象属性的延迟加载。

  • 他说使用静态 IoC 查找调用来解析和延迟加载对象的对象。
  • 我说这违反了 SRP,并使用所属服务来解析该对象。

那么,您将如何处理遵循 IoC 和 SRP 的延迟加载?

您无法对延迟加载的属性进行单元测试。他反驳了这一说法:“您已经对 UserStatsService 进行了单元测试 - 这就是您的代码覆盖率。”这是一个有效的观点,但该属性尚未经过“完整”覆盖的测试。

设置/代码模式:

  • 项目使用严格的依赖注入规则(注入所有服务、存储库等的构造函数中)。
  • 项目通过 Castle 使用 IoC(但可以是任何东西,比如 Unity)。

下面是一个例子。

public class User
{
  public Guid UserId { get; set; }

  private UserStats _userStats;
  // lazy-loading of an object on an object
  public UserStats UserStats
  {
    get
    {
      if (_userStats == null)
      {
        // ComponentsLookup is just a wrapper around IoC 
        // Castle/Unity/etc.
        _userStats = 
          ComponentsLookup
            .Fetch<UserStatsService>()
              .GetByUserId(this.UserId);
      }
      return _userStats;
    }
  }
}

上面显示了延迟加载对象的示例。我建议不要使用它,只要您需要该对象,就可以从 UI 层访问 UserStatsService。

编辑:下面的一个答案让我想起了 NHibernate 延迟加载的技巧,即虚拟化你的属性,允许 NHibernate 创建延迟加载本身的过载。很光滑,是的,但是我们没有使用 NHibernate。

没有人真正解决延迟加载的问题。一些好的文章和SO问题很接近:

我确实看到了延迟加载的好处。不要误会我的意思,我所做的只是延迟加载复杂类型及其子类型,直到我切换到忍者的 DI 方式。好处在于 UI 层,其中显示用户的统计信息,例如显示在 100 行的列表中。但对于 DI,现在您必须引用几行代码来获取用户统计信息(不违反 SRP 且不违反 Demeter 定律),并且它必须走这条漫长的查找路径 100 多次。

是的,添加缓存并确保 UserStatsService 被编码为用作单例模式,可以大大降低性能成本。

但我想知道是否还有其他人有一个[顽固的]开发人员,他们不会完全屈服于 IoC 和 DI 规则,并且具有有效的性能/编码点来证明解决方法的合理性。

A fellow developer and I are conversing (to put it lightly) over Lazy-Loading of Properties of an object.

  • He says to use a static IoC lookup call for resolution and Lazy-Loading of objects of an object.
  • I say that violates SRP, and to use the owning Service to resolve that object.

So, how would you handle Lazy-Loading following IoC and SRP?

You cannot Unit test that lazy-loaded property. He rebuttles that one saying, "you already have unit tests for the UserStatsService - there's your code coverage." A valid point, but the property remains untested though for "complete" coverage.

Setup / code patterns:

  • Project is using strict Dependency Injection rules (injected in the ctors of all services, repositories, etc).
  • Project is using IoC by way of Castle (but could be anything, like Unity).

An example is below.

public class User
{
  public Guid UserId { get; set; }

  private UserStats _userStats;
  // lazy-loading of an object on an object
  public UserStats UserStats
  {
    get
    {
      if (_userStats == null)
      {
        // ComponentsLookup is just a wrapper around IoC 
        // Castle/Unity/etc.
        _userStats = 
          ComponentsLookup
            .Fetch<UserStatsService>()
              .GetByUserId(this.UserId);
      }
      return _userStats;
    }
  }
}

The above shows an example of lazy-loading an object. I say not to use this, and to access UserStatsService from the UI layer wherever you need that object.

EDIT: One answer below reminded me of the NHibernate trick to lazy-loading, which is to virtualize your property, allowing NHibernate to create an over-load of the lazy-loading itself. Slick, yes, but we are not using NHibernate.

No one really tackles the matter of Lazy-Loading. Some good articles and SO questions get close:

I do see a benefit of lazy-loading. Don't get my wrong, all I did was lazy-loading of my complex types and their sub-types until I switched to the D.I.-ways of the ninja. The benefit is in the UI layer, where a user's stats is displayed, say, in a list with 100 rows. But with DI, now you have to reference a few lines of code to get that user stats (to not violate SRP and not violate the law-of-Demeter), and it has to walk this long path of lookups 100+ times.

Yes yes, adding caching and ensuring the UserStatsService is coded to be used as a Singleton pattern greatly lower the performance cost.

But I am wondering if anyone else out there has a [stubborn] developer that just won't bend to the IoC and D.I. rules completely, and has valid performance/coding points to justify the work-arounds.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一抹苦笑 2024-08-24 16:30:46

实体本身不应该承担延迟加载的责任。这是一个基础设施问题,其解决方案将在别处。

假设一个实体在两个不同的上下文中使用。第一个,它的子项被大量使用并且被急切地加载。在第二种情况下,它们很少使用并且是延迟加载的。这也是实体所关心的吗?配置会是什么样子?

NHibernate 通过代理实体类型来回答这些问题。 IList 类型的属性由基础设施设置为了解延迟加载的实现。该实体仍然幸福地浑然不觉。还处理父引用(如您的问题),只需要一个简单的属性。

现在关注点在实体之外,基础设施 (ORM) 负责确定上下文和配置(例如急切/延迟加载)。

Entities themselves should not have the responsibility of lazy loading. That is an infrastructural concern whose solution will lie elsewhere.

Let's say an entity is used in two separate contexts. In the first, its children are used heavily and are eagerly-loaded. In the second, they are used rarely and are lazy-loaded. Is that also the entity's concern? What would the configuration look like?

NHibernate answers these questions by proxying the entity type. A property of type IList<Entity> is set by the infrastructure to an implementation which knows about lazy loading. The entity remains blissfully unaware. Parent references (like in your question) are also handled, requiring only a simple property.

Now that the concern is outside the entity, the infrastructure (ORM) is responsible for determining context and configuration (like eager/lazy loading).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文