如何正确地使用深度对象图和许多依赖关系进行手动DI

时间:2010-03-11 14:00:30

标签: dependency-injection guice

我相信这些问题已经以某种方式或其他方式提出,但我还没有得到它。

我们做了一个GWT项目,我的项目负责人不允许使用GIN / Guice作为DI框架(新的程序员不会理解它,他认为)所以我尝试手动进行DI。

现在我遇到了深度对象图的问题。 UI中的对象层次结构如下所示:

AppPresenter-> DashboardPresenter-> GadgetPresenter-> GadgetConfigPresenter

对象层次结构树中的GadgetConfigPresenter方式有一些依赖项,如CustomerRepository,ProjectRepository,MandatorRepository等。

因此,创建GadgetConfigPresenter的GadgetPresenter也具有这些依赖关系,依此类推,直到创建AppPresenter的应用程序的入口点。

  • 这是手动DI的工作方式吗?
  • 这是不是意味着我在启动时创建所有依赖项,即使我不需要它们?
  • 像GIN / Guice这样的DI框架会帮助我吗?

2 个答案:

答案 0 :(得分:8)

你写的那个

  

GadgetPresenter创建GadgetConfigPresenter [。]

而不是直接创建GadgetConfigPresenter个实例,而GadgetPresenter应该take a dependency on an Abstract Factory可以为其创建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);

请注意我们经常打破依赖关系链,确保每个消费者的依赖关系数量永远不会变得太大。

原则上,这意味着您必须在启动时创建所有依赖项,除非您实现lazy loading strategy

管理生命周期这样的事情就是DI容器非常有用的东西,但完全有可能只用following DI patterns and principles编写整个应用程序。

总而言之,如果可能的话,我仍然会推荐一个DI容器。

答案 1 :(得分:0)

您可以使用Context接口进行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();
  }
}