Как сделать ручной DI с глубокими графами объектов и многими зависимостями правильно

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

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

Теперь у меня есть проблема с глубокими графами объектов. Иерархия объектов от UI похожа на это:

AppPresenter-> DashboardPresenter-> GadgetPresenter-> GadgetConfigPresenter

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

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

  • Действительно ли это - путь руководство, DI, как предполагается, работает?
  • разве это не означает, что я создаю все зависимости во время начальной загрузки, даже мне не нужны они?
  • платформа DI хотела бы GIN/Guice, помогают мне здесь?
7
задан Fabian 11 March 2010 в 14:00
поделиться

2 ответа

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

GadgetPresenter создает GadgetConfigPresenter [.]

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

При использовании Constructor Injection на всех этапах подключение Poor Man's 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-контейнер, если это вообще возможно.

10
ответ дан 7 December 2019 в 01:19
поделиться

Вы можете выполнять DI с помощью контекстных интерфейсов. Это не сложно и довольно просто.

Контекстный интерфейс - это класс, который предоставляет все привязки из конфигурации модуля 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();
  }
}
0
ответ дан 7 December 2019 в 01:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: