Инверсия управления, внедрение зависимостей с SRP и отложенная загрузка

Мы с товарищем-разработчиком обсуждаем (мягко говоря) ленивую загрузку свойств объекта.

  • Он говорит использовать статический вызов поиска IoC для разрешения и отложенной загрузки объектов объекта.
  • Я говорю, что это нарушает SRP, и использую сервис-владелец для разрешения этого объекта.

Итак, как бы вы справились с отложенной загрузкой после IoC и SRP?

Вы не можете провести модульное тестирование этого свойства с отложенной загрузкой. Он опровергает это высказывание: «У вас уже есть модульные тесты для UserStatsService - вот и покрытие вашего кода». Верно, но свойство остается непроверенным на предмет «полного» покрытия.

Шаблоны настройки / кода:

  • В проекте используются строгие правила внедрения зависимостей (внедряются в ctors всех сервисов, репозиториев и т. Д.).
  • В проекте используется IoC через Castle (но может быть что угодно, например 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;
    }
  }
}

Выше показан пример отложенной загрузки объекта. Я говорю не использовать это и получить доступ к UserStatsService из уровня пользовательского интерфейса, где бы вам ни понадобился этот объект.

РЕДАКТИРОВАТЬ: один ответ ниже напомнил мне о трюке NHibernate с ленивой загрузкой, который заключается в виртуализации вашего свойства, позволяя NHibernate создавать перегрузку самой ленивой загрузки. Отлично, да, но мы не используем NHibernate.

На самом деле никто не занимается ленивой загрузкой. Близятся несколько хороших статей и ТАК вопросы:

Я действительно вижу преимущество отложенной загрузки. Не поймите меня неправильно, все, что я делал, это лениво загружал свои сложные типы и их подтипы, пока я не переключился на методы D.I. ниндзя. Преимущество заключается в уровне пользовательского интерфейса, где статистика пользователя отображается, например, в списке из 100 строк. Но с DI теперь вам нужно ссылаться на несколько строк кода, чтобы получить эту статистику пользователя (чтобы не нарушать SRP и не нарушать закон Деметры), и он должен пройти этот длинный путь поиска более 100 раз.

Да, да, добавление кеширования и обеспечение того, чтобы UserStatsService был закодирован для использования в качестве шаблона Singleton, значительно снижает затраты на производительность.

Но мне интересно, есть ли у кого-нибудь еще [упрямый] разработчик, который просто не подчинится IoC и D.I. rules полностью и имеет допустимые точки производительности / кодирования, чтобы оправдать обходные пути.


person eduncan911    schedule 12.01.2010    source источник


Ответы (1)


Сами сущности не должны нести ответственность за отложенную загрузку. Это инфраструктурная проблема, решение которой нужно искать в другом месте.

Скажем, сущность используется в двух разных контекстах. В первом случае его дочерние элементы используются интенсивно и быстро загружаются. Во втором они используются редко и лениво загружаются. Это тоже забота организации? Как бы выглядела конфигурация?

NHibernate отвечает на эти вопросы, проксируя тип объекта. Свойство типа IList<Entity> устанавливается инфраструктурой для реализации, которая знает о ленивой загрузке. Сущность остается в блаженном неведении. Родительские ссылки (как в вашем вопросе) также обрабатываются, для чего требуется только простое свойство.

Теперь, когда проблема находится вне сущности, инфраструктура (ORM) отвечает за определение контекста и конфигурации (например, нетерпеливую / ленивую загрузку).

person Bryan Watts    schedule 12.01.2010
comment
+1 Полностью согласен, что забота должна быть вне сущности. С NHibernate (я знаю, как он перегружает свойство отложенной загрузки, да) это было бы легко. Но мы не используем NHibernate. Кроме того, в описанном вами редком случае (без NHibernate) это будет склонно к использованию большого количества памяти - при загрузке дочерних свойств на уровне репо. Отсюда и концепция ленивой загрузки для случаев, когда требуется высокая производительность. Я продолжу изучать другие закономерности. - person eduncan911; 12.01.2010
comment
Реализация отложенной загрузки под IList<> не зависит от NHibernate; это просто простой пример. Его главное преимущество перед статическими вызовами - это контекстная чувствительность. Когда вы помещаете статический вызов в класс, вы автоматически делаете его контекст всем AppDomain, требуя, чтобы каждый экземпляр работал точно так же. Допустим, у вас есть страница со списком и экспорт для объекта. Вы можете отложить загрузку страниц списка, но нетерпеливую загрузку для экспорта (поскольку вы знаете, что все обработаете). Такое контекстное поведение (и другие, такие как размеры пакетов) по существу исключаются статическими вызовами. - person Bryan Watts; 13.01.2010