使用Dagger在构造函数上进行依赖注入

Ale*_*lex 28 android dependency-injection dagger

所以,我正在重新设计我的Android应用程序以使用Dagger.我的应用程序庞大而复杂,我最近遇到了以下情况:

对象A需要一个特殊的DebugLogger实例,这是一个完美的注入候选者.我可以通过A的构造函数注入它,而不是绕过记录器.这看起来像这样:

class A
{
    private DebugLogger logger;

    @Inject
    public A(DebugLogger logger)
    {
        this.logger = logger;
    }

    // Additional methods of A follow, etc.
}
Run Code Online (Sandbox Code Playgroud)

到目前为止这是有道理的.但是,A需要由另一个类B构造.必须构造A的多个实例,所以遵循Dagger的做事方式,我简单地将一个注入Provider<A>到B中:

class B
{
    private Provider<A> aFactory;

    @Inject
    public B(Provider<A> aFactory)
    {
        this.aFactory = aFactory;
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,到目前为止还不错.但等等,突然A需要额外的输入,例如一个称为"数量"的整数,这对于它的构造至关重要.现在,我的A构造函数需要如下所示:

@Inject
public A(DebugLogger logger, int amount)
{
...
}
Run Code Online (Sandbox Code Playgroud)

突然,这个新参数干扰了注射.而且,即使这确实有效,除非我弄错了,否则在从提供者检索新实例时我无法传递"金额".我可以在这里做几件事,我的问题是哪一个是最好的?

我可以通过添加一个setAmount()预期在构造函数之后调用的方法来重构A. 然而,这是丑陋的,因为它迫使我延迟A的构造直到"数量"被填入.如果我有两个这样的参数,"数量"和"频率",那么我将有两个安装者,这将意味着要么复杂的检查以确保在调用两个setter之后恢复A的构造,或者我将不得不在混合中添加第三个方法,如下所示:

(Somewhere in B):

A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();
Run Code Online (Sandbox Code Playgroud)

另一种选择是我不使用基于构造函数的注射并使用基于场的注射.但现在,我必须把我的田地公之于众.这对我来说并不合适,因为现在我有义务将我班级的内部数据透露给其他班级.

到目前为止,我能想到的唯一优雅的解决方案是为提供者使用基于字段的注入,如下所示:

class A
{
    @Inject
    public Provider<DebugLogger> loggerProvider;
    private DebugLogger logger;

    public A(int amount, int frequency)
    {
        logger = loggerProvider.get();
        // Do fancy things with amount and frequency here
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

即使如此,我也不确定时间,因为我不确定Dagger是否会在调用构造函数之前注入提供程序.

有没有更好的办法?我只是错过了Dagger的工作原理吗?

Jak*_*ton 54

您所说的是辅助注射,目前Dagger不支持任何自动方式.

您可以使用工厂模式解决此问题:

class AFactory {
  @Inject DebugLogger debuggLogger;

  public A create(int amount, int frequency) {
    return new A(debuggLogger, amount);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以注入此工厂并使用它来创建以下实例A:

class B {
  @Inject AFactory aFactory;

  //...
}
Run Code Online (Sandbox Code Playgroud)

当你需要创建一个A带有"数量"和"频率"的产品时,你可以使用工厂.

A a = aFactory.create(amount, frequency);
Run Code Online (Sandbox Code Playgroud)

这允许A具有final记录器,数量和频率字段的实例,同时仍然使用注入来提供记录器实例.

Guice有一个辅助注射插件,可以为您自动创建这些工厂.目前已经讨论有关他们可以添加,但什么也没有在写这篇文章的决定恰当的方式匕首邮件列表上.