使用依赖注入优于新的优势是什么?

时间:2016-07-04 19:35:05

标签: java dependency-injection guice

在以下代码中(使用Guice和Dependency Injection):

public class Main {

public static class Foo {
    private FooInterface anInterface;

    @Inject
    Foo(FooInterface anInterface) {
        this.anInterface = anInterface;
    }

    public void talk() {
        anInterface.talk();
    }

}

interface FooInterface {
    void talk();
}

static class English implements FooInterface {

    @Override
    public void talk() {
        System.out.println("Hello");
    }
}

static class Spanish implements FooInterface {

    @Override
    public void talk() {
        System.out.println("Hola");
    }
}

public static class Module extends AbstractModule {
    @Override
    protected void configure() {
        bind(FooInterface.class).to(English.class);
    }

}

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

    Foo regularInstanceFoo = new Foo(new Spanish());
    regularInstanceFoo.talk();

}
}

使用Guice(injectorInstanceFoo)获取Foo的实例超过"直接"有什么好处?方式(regularInstanceFoo)

1 个答案:

答案 0 :(得分:1)

让我们谈谈使用您的玩具代码的程序可能是什么样的。想象一下它是一个控制台应用程序,绘制开始屏幕时需要talk()方法。在您的设置中,您可以控制是否绑定了英语或西班牙语实例(使用其他模块; 不要在模块内部使用条件逻辑)。使用依赖注入的第一个好处是,您的逻辑可以轻松控制在代码的其他地方提供哪种语言,这样调用者就不必拥有逻辑来选择他们想要的实现。

它还使单元测试变得更加容易 - 我将在这里进一步深入说明:您想要编写一些测试代码来测试为登录屏幕创建消息的行为。在此课程LoginScreen中,根据您的区域设置,您@Inject使用不同的FooInstance

public class LoginScreen {
    private final String message;

    @Inject
    public LoginScreen(FooInstance foo) {
        foo.talk();
        // use other methods on foo that you didn't write
        //
    }
}

现在,您要测试 <{em> LoginScreen的行为 - FooInstance或其实现的行为。这个语法使用JUnit和Mockito(为了简洁),但它是相当自我解释的:

public class LoginScreenTest() {
    @Test
    public void testMessage() {
        FooInstance foo = mock(FooInstance.class);
        LoginScreen screen = new LoginScreen(foo);
        verify(foo).talk();
    }
}

在这种情况下,我不关心FooInstanceEnglish还是Spanish 。我相信这些类已经正确实现(我会在不同的单元测试中为它们编写测试)。 FooInstance合同talk()会向控制台写一个问候语 - 所有我关心的是LoginScreen抓住了这个问候语,但我没有&#39关心FooInstance表现正确。

这在您的示例中并不是非常有意义,因为我可以调用FooInstance foo = new English();。但是,如果代替System.out.println()FooInstance必须加载数据库呢?或者如果它弹出一个Swing对话框怎么办?或者如果FooInstance有其他依赖项怎么办?在所有这三种情况下,在测试中调用new English()会非常痛苦。

注意:这不是区域化的最佳方式,但我只是运行您的示例。