27

我(想我)理解依赖注入的目的,但我只是不明白为什么我需要像 Guice 这样的东西来做到这一点(好吧,显然我不需要Guice,但我的意思是为什么使用它会有好处)。假设我有类似这样的现有(非 Guice)代码:

public SomeBarFooerImplementation(Foo foo, Bar bar) {
    this.foo = foo;
    this.bar = bar;
}

public void fooThatBar() {
    foo.fooify(bar);
}

在更高级别的某个地方,也许在我的 main() 中,我有:

public static void main(String[] args) {
    Foo foo = new SomeFooImplementation();
    Bar bar = new SomeBarImplementation();
    BarFooer barFooer = new SomeBarFooerImplementation(foo, bar);

    barFooer.fooThatBar();
}

现在我基本上得到了依赖注入的好处,对吧?更容易的可测试性等等?当然,如果您愿意,也可以轻松更改 main() 以从配置中获取实现类名,而不是硬编码。

据我了解,要在 Guice 中做同样的事情,我会做类似的事情:

public SomeModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Foo.class).to(SomeFooImplementation.class);
        bind(Bar.class).to(SomeBarImplementation.class);
        bind(BarFooer.class).to(SomeBarFooerImplementation.class);
    }
}

@Inject
public SomeBarFooerImplementation(Foo foo, Bar, bar) {
    this.foo = foo;
    this.bar = bar;
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SomeModule());
    Foo foo = injector.getInstance(Foo.class);

    barFooer.fooThatBar();
}

是这样吗?在我看来,它只是语法糖,而不是特别有用的语法糖。如果将“new XxxImplementation()”的东西分解成一个单独的模块而不是直接在 main() 中执行它有一些优势,那么无论如何不用 Guice 也很容易做到。

所以我觉得我错过了一些非常基本的东西。您能否向我解释一下 Guice 方式的优势?

提前致谢。

4

2 回答 2

20

在现实生活和更大的应用程序中,组件的级别不仅仅是 2(如您的示例中所示)。它可能是 10 个或更多。需求和依赖关系也可以随心所欲地改变。

当进行“手动 DI”时,这意味着您必须手动更改所有组件的所有路径及其传递依赖项。这可能只是一些工作。

使用“真正的 DI”(即 Guice、Spring、CDI),您只需更改一两个受影响的组件和接线(ModuleGuice 上的)即可。

另外:想象一下,您的一些深层嵌套组件位于应由其他组件调用的工厂中。在 Guice 中,您只需注入 a 即可Provider<Foo>。手动执行时,您必须将工厂的依赖项及其实例化拖到整个代码中。

这更令人沮丧。

编辑澄清最后一点:图像你有一些深度嵌套的逻辑(10个或更多级别)。在底部,您的逻辑将基于某个值创建一个工厂,例如实际温度:如果“暖”则Cooler需要 a,如果“冷”则需要 a Heater。两者都有接口TemperatureAdjustor。两者都Cooler需要Heater许多不同的依赖项才能完成工作。

由于因素的实例类型,您无法在其中创建一个main并将其传递到需要的地方。因此,您最终会将两个工厂的依赖关系交给需要它们的位置。(“很多依赖”加上“很多依赖”是“太多而无法保持理智”)

使用像 Guice 这样的“真正的 DI”,您可以使用它AssistedInject来避免依赖关系的混乱,并且只将实际的业务价值(这里:温度)提供给工厂并完成。工厂的接线是在模块中完成的。

于 2012-10-19T16:45:46.647 回答
2

手工编码的 DI 是真正的 DI。

当您考虑 IOC 容器的复杂性时,您必须更深入地了解为什么要使用它。我长期反对 IOC,能够设计和实现干净、快速、整洁的手工编码、真正的 DI。但我接受了容器的想法,因为它们有助于标准化实现并提供更多基于配置的机制来向依赖关系方程添加新的决策。其他人已经为您编写了它们,因此您不必花时间手动编码。

许多人说在大应用程序中使用容器,在小应用程序中手动编写代码。我说反了。小型应用程序的性能不受限制。追求使用打包容器的实现速度。在您将花费大量时间进行优化的大型应用程序中,请考虑手动编码您的 DI。

编辑:但让我补充一点,IOC Container 或手工编码,始终使用 DI 和代码到界面 - 无论您正在处理的应用程序多么简单。即使在小应用程序中也能编写出色的代码。

于 2013-07-16T18:28:31.993 回答