用匕首对构造函数依赖注入(Using Dagger for dependency injection

2019-08-31 07:34发布

所以,我目前正在重新设计我的Android应用使用匕首 。 我的应用程序是大型,复杂的,我最近遇到以下情形传来:

对象A需要一个特殊的DebugLogger实例,它是注射用完美的候选人。 而是绕过记录的我可以刚刚经历了一场的构造注入它。 这看起来是这样的:

class A
{
    private DebugLogger logger;

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

    // Additional methods of A follow, etc.
}

到目前为止,这是有道理的。 然而,A需要由另一个类B.多个实例构造必须构造,所以下面做事的匕首的方式,我简单的注入Provider<A>到B:

class B
{
    private Provider<A> aFactory;

    @Inject
    public B(Provider<A> aFactory)
    {
        this.aFactory = aFactory;
    }
}

好了,好为止。 别急,突然需要额外的投入,如所谓的“量”的整数,它的建设是至关重要的。 现在,我的构造函数需要这个样子:

@Inject
public A(DebugLogger logger, int amount)
{
...
}

突然,这个新的参数,干扰注射。 此外,即使这没有工作,就没有办法,我在“量”通过从供应商获取一个新的实例时,除非是我弄错了。 有几件事情,我可以在这里做的,而我的问题是哪一个是最好的?

我可以通过添加重构一个setAmount()预计在构造函数后要调用的方法。 这是丑陋的,但是,因为它迫使我延迟的建设,直到“量”已被填补。如果我有两个这样的参数,“量”和“频率”,那么我将有两个setter方法,这将意味着无论是复杂的检查,以确保那建造重新开始制定者都被称为后,或者我会还添加第三个方法混进去,就像这样:

(Somewhere in B):

A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();

另一种选择是,我不使用基于构造函数的注入,并与基于现场的注射去。 但现在,我必须让我的领域公开。 这不跟我坐好,因为现在我有义务来我班的内部资料透露给其他类。

到目前为止,只有几分优雅的解决方案,我能想到的是使用基于字段的注射供应商,就像这样:

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
        ...
    }
}

即使如此,我不确定的时间,因为我不知道如果我的构造函数被调用之前匕首将注入的供应商。

有没有更好的办法? 我只是失去了一些东西约匕首是如何工作的?

Answer 1:

你所谈论的辅助注射,而不是目前的匕首在任何自动的方式支持是已知的。

您可以解决此同工厂模式:

class AFactory {
  @Inject DebugLogger debuggLogger;

  public A create(int amount, int frequency) {
    return new A(debuggLogger, amount);
  }
}

现在,你可以注入这家工厂,并用它来创建的实例A

class B {
  @Inject AFactory aFactory;

  //...
}

当你需要创建A与你的“量”和“频率”你使用工厂。

A a = aFactory.create(amount, frequency);

这允许A具有final同时仍然使用注射,以提供记录器实例的记录器,量和频率域的实例。

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



Answer 2:

什么杰克的帖子说的是完全正确的。 这就是说,我们(的一些谷歌的民俗谁与吉斯和匕首工作的)正在对“辅”或自动工厂产生的替代版本,这应该是使用由吉斯或匕首或独立的 - 也就是说,它将生成的工厂类的源代码给你。 这些工厂类将(如果合适的话)是可注射的任何标准JSR-330将类。 但它尚未发布。

待这样一个解决方案,杰克沃顿商学院的做法是不可取的。



Answer 3:

你是因为你是混合注射剂和非注射剂在你的构造有问题。 用于注射的一般规则,将节省您吨心痛,并保持你的代码干净是:

  1. 注射剂可以要求在其构造其他注射,而不是newables。

  2. Newables可以要求在其构造其他newables但不适合注射。

注射剂是服务类型的对象,做一个CreditCardProcessor,MusicPlayer等工作,这样即对象

Newables是值类型的对象,例如信用卡式,歌曲等



Answer 4:

杰克的职位是伟大的,但有更简单的方法。 谷歌创建AutoFactory库在编译的时候自动创建的工厂。

首先,创建一流A@AutoFactory注释和@Provided注入参数注释:

@AutoFactory
public class A {

    private DebugLogger logger;

    public A(@Provided DebugLogger logger, int amount, int frequency) {
        this.logger = logger;
    }
}

然后库创建AFactory在编译时类。 所以,你只需要在工厂注入到构造B类。

public class B {

    private final AFactory aFactory;

    @Inject
    public B(AFactory aFactory) {
        this.aFactory = aFactory;
    }

    public A createA(int amount, int frequency) {
        return aFactory.create(amount, frequency);
    }
}


Answer 5:

我只想补充一点,这个问题后通过几年已经公布,现在有一个叫库AssistedInject已经在广场创建了由杰克和朋友,解决了完全相同的问题,并与匕首2完全兼容。

你可以在这里找到它: https://github.com/square/AssistedInject



文章来源: Using Dagger for dependency injection on constructors