Как правильно выполнить ручной DI с глубокими графами объектов и множеством зависимостей - PullRequest
7 голосов
/ 11 марта 2010

Я полагаю, что эти вопросы задавались тем или иным способом, но я еще не получил их.

Мы выполняем проект GWT, и мой руководитель проекта не разрешил использовать GIN / Guice в качестве структуры DI (новые программисты не поймут этого, утверждал он), поэтому я пытаюсь сделать DI вручную.

Теперь у меня проблема с графами глубоких объектов. Иерархия объектов из пользовательского интерфейса выглядит следующим образом:

AppPresenter-> DashboardPresenter-> GadgetPresenter-> GadgetConfigPresenter

Путь GadgetConfigPresenter вниз по дереву иерархии объектов имеет несколько зависимостей, таких как CustomerRepository, ProjectRepository, MandatorRepository и т. Д.

Таким образом, GadgetPresenter, который создает GadgetConfigPresenter, также имеет эти зависимости и так далее, вплоть до точки входа приложения, которое создает AppPresenter.

  • Так должен работать ручной DI?
  • не означает ли это, что я создаю все зависимости во время загрузки, даже если они мне не нужны?
  • Поможет ли мне здесь DI-фреймворк, такой как GIN / Guice?

Ответы [ 2 ]

8 голосов
/ 11 марта 2010

Вы пишете, что

GadgetPresenter, который создает GadgetConfigPresenter [.]

Вместо непосредственного создания GadgetConfigPresenter экземпляров, GadgetPresenter должен взять зависимость от абстрактной фабрики , которая может создать для нее GadgetConfigPresenter экземпляров. Это подталкивает внутренние зависимости GadgetConfigPresenter к фабрике.

Используя Конструкторское внедрение полностью, ваша DI проводка для бедного человека должна выглядеть примерно так (извиняюсь за синтаксис C #):

var customerRepository = new CustomerRepository(/*...*/);
var projectRepository = new ProjectRepository(/*...*/);
var mandatorRepository = new MandatorRepository(/*...*/);

var gadgetConfigPresenterFactory = 
    new GadgetConfigPresenterFactory(
        customerRepository,
        projectRepository,
        mandatorRepository);

var gadgetPresenter = new GadgetPresenter(gadgetConfigPresenterFactory);
var dashboardPresenter = new DashboardPresenter(gadgetPresenter);
var appPresenter = new AppPresenter(dashboardPresenter);

Обратите внимание, как мы часто разрываем цепочку зависимостей , гарантируя, что количество зависимостей для каждого потребителя никогда не станет слишком большим.

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

Такие вещи, как управление временем жизни, - это именно та вещь, в которой DI-контейнер может быть чрезвычайно полезен, но вполне возможно написать целое приложение, просто следуя шаблонам и принципам DI .

В целом, я все равно рекомендовал бы Контейнер DI, если это вообще возможно.

0 голосов
/ 12 марта 2010

Вы можете сделать DI, используя контекстные интерфейсы. Это не сложно, и довольно прямо вперед.

Интерфейс Context - это класс, который предоставляет все привязки из конфигурации модуля guice.

Это пример этого, где я предполагаю, что AppPresenter + DashboardPresenter находится в одном пакете и нуждается в одном «контексте», тогда как GadgetPresenter и GadgetConfigPresenter находятся в другом пакете и нуждается в другом «контексте». Количество контекстов и способы их обработки полностью зависит от пользователя.

/**
 * The dependencies that need to be injected for package1
 */
public interface SomePackageContext {
  GadgetPresenter getGadgetPresenter();
  GadgetConfigPresenter getGadgetConfigPresenter();
}

/**
 * The dependencies that need to be injected for package2
 */
public interface OtherPackageContext {
  // These methods can take arguments..
  AppPresenter getAppPresenter(Args..);
  DashboardPresenter getDashboardPresenter(Args..);
}

/**
 * All of the DI needed in our project.
 *
 * <p>We don't need the two interfaces above, we can put 
 * everything in this interface if we have a small
 * project where layering is not a big issue.
 */
public interface PresenterContext 
    extends SomePackageContext, OtherPackageContext {
}


public class MockPresenterContext implements PresenterContext {
  ...
}

public class RealPresenterContext implements PresenterContext {
  // This is similar to bind(...) in guice
  public AppPresenter getAppPresenter(Args..) {
    return new AppPresenter(this, otherargs...);
  }
  public DashboardPresenter getDashboardPresenter(Args..) {
    return new DashboardPresenter(this, otherargs...);
  }
  public GadgetPresenter getGadgetPresenter() {
    return new GadgetPresenter(this);
  }
  public GadgetConfigPresenter getGadgetConfigPresenter() {
    return new GadgetConfigPresenter();
  }
}

public class DashboardPresenter {

  // @Inject
  private final GadgetPresenter gadgetPresenter;

  /*
   * We inject everything using the SomePackageContext.
   */
  public DashboardPresenter(SomePackageContext ctxt) {
    this.gadgetPresenter = ctxt.getGadgetPresenter();
  }
}
...