吉斯的宗旨

2022-09-01 16:10:01

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

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

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

在更高层次的地方,也许在我的主要(),我有:

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的方式是如何有利的?

提前致谢。


答案 1

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

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

使用“真正的DI”(即Guice,Spring,CDI),您只需更改一两个受影响的组件和布线(在Guice上),仅此而已。Module

另外:假设您的一些深度嵌套组件位于工厂中,这些工厂应由其他组件调用。在Guice中,你只需注入一个,就是这样。手动执行此操作时,必须在整个代码中拖动工厂的依赖项及其实例化。Provider<Foo>

这更令人沮丧。

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

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

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


答案 2

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

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

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

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


推荐