【问题标题】:Dagger 2: Injecting member which has constructor parametersDagger 2:注入具有构造函数参数的成员
【发布时间】:2018-07-12 21:03:35
【问题描述】:

我正在尝试通过实现一个虚拟的 Java 控制台应用程序来学习 Dagger 2,并且我有这个类。

public class Lemonader {

    private Water water;
    private Lemon lemon;
    private Sugar sugar;

    private WaterCooler waterCooler;

    public Lemonader(Water water, Lemon lemon, Sugar sugar) {
        this.water = water;
        this.lemon = lemon;
        this.sugar = sugar;
        this.waterCooler = new WaterCooler(water); // TODO remove hard dependency via Dagger 2
    }

    public void makeLemonade() {
        System.out.println("Making lemonade..");
        waterCooler.coolItUpTheWater();
        System.out.println("Lemonade has been made with " + water.getAmount() + " liters of water, " + sugar.getAmount() + " kilograms of sugar and " + lemon.getQuantity() + " lemons.");
    }

这就是我创建Lemonader 对象的方式

public class LemonaderHelper {

    public Lemonader getLemonader(int waterAmount, boolean isWaterHot, int lemonQuantity, int sugarAmount) {
        Water w = new Water(); 
        w.setAmount(waterAmount);
        w.setCold(isWaterHot);

        Lemon l = new Lemon(); 
        l.setQuantity(lemonQuantity);

        Sugar s = new Sugar(); 
        s.setAmount(sugarAmount);

        Lemonader lemonader = new Lemonader(w, l, s); 

        return lemonader;
    }

}

我想通过 Dagger 2 注入 Lemonader 的 WaterCooler 成员,但我找不到传递来自 Lemonader 构造函数的 water 实例的方法(我尝试创建提供 Water 和 @987654328 的模块@. 它有效,但它正在创建一个新的 water 实例,而不是传递来自 Lemonader 构造函数的实例)。我怎样才能通过 Dagger 2 做到这一点?任何帮助将不胜感激。

【问题讨论】:

    标签: java dependency-injection dependencies dagger-2 dagger


    【解决方案1】:

    Dagger 2 用于在图中自动提供依赖项。因为 WaterCooler 只需要一个依赖项并且它不在图表中,所以使用 WaterCooler 作为实现细节,我会像你一样拥有它:

    this.waterCooler = new WaterCooler(water);
    

    但是,如果您希望能够替换实现,您可能会考虑注入一个相关对象,该对象具有一个方法,该方法接受一个参数(水)并返回一个冷水源。你可能认为这只是一个构造函数包装器,但它实际上是遵循factory method pattern

    interface SourceOfCoolWaterFactory {
      /** Probably returns a water cooler. Might return a refrigerator. */
      SourceOfCoolWater createCoolWaterSource(Water water);
    }
    

    啊哈!现在您可以注入SourceOfCoolWaterFactory,并创建一个简单调用new WaterCooler(water) 的实现。这消除了您在代码注释中确定的硬依赖,允许您替换任何您想要的实现——包括用于测试的模拟。重要的是,这还允许您混合来自图表的依赖项,例如当您的 WaterCooler 电池耗尽时:

    public class WaterCoolerFactory implements {
      private final Provider<Electricity> electricityProvider;
    
      @Inject public WaterCoolerFactory(Provider<Electricity> electricityProvider) {
        this.electricityProvider = electricityProvider;
      }
    
      @Override
      public SourceOfCoolWater createCoolWaterSource(Water water) { 
        return new WaterCooler(water, electricityProvider.get());
      }
    }
    

    您现在有一个可以在 Dagger 中绑定的实现,允许您的 Lemonader 注入 SourceOfCoolWaterFactory 而不是直接调用 WaterCooler 构造函数,并正确地将图形生成的依赖项(例如 Electricity)添加到您的构造函数依赖项(例如 Water)中。


    虽然 Dagger 实际上并没有提供任何自动化工厂的方法,但 Google 有一个名为 AutoFactory 的姊妹项目,它可以为任何 JSR-330 依赖注入框架(例如 Dagger、Guice 和 Spring)生成可注入工厂,并且它位于值对象和 Java 服务的代码生成器中,位于一个名为 Google Auto 的包中。使用 AutoFactory,您可以使用一些注解来注解 WaterCooler,AutoFactory 将创建一个您可以在 Dagger 中绑定的实现:

    // If you don't specify a class to implement, Dagger will write one.
    // This might be useful if you only have one implementation, but you
    // want to substitute it with a mock in tests.
    @AutoFactory(implementing = SourceOfCoolWaterFactory.class)
    public WaterCooler(Water water, @Provided Electricity electricity) {
      // ...
    }
    

    【讨论】:

    • 我已经阅读了一段时间的 DI 和 DI 框架,我发现术语 newable 和 injectionable。看起来我的 Water 类是一个 newable,我不应该通过 DI 框架注入它,而是应该从可注入的构造函数中删除它并通过 new 运算符创建它。所以看起来我的应用程序设计存在一些问题,在将 Dagger 2 用作 DI 框架之前我必须解决这些问题。
    • @Tartar 不要太担心打响指,让每个绑定都可以完美替代;软件设计通常太混乱了。 Dagger 可以为您提供一种自动化的方式来随着事物的发展以及您需要在对象创建中获得灵活性而进行连接,从这个意义上说,您可以务实地从最明显和最有价值的绑定开始慢慢添加绑定。
    猜你喜欢
    • 2015-11-11
    • 1970-01-01
    • 1970-01-01
    • 2015-10-01
    • 2018-07-04
    • 2019-02-23
    • 1970-01-01
    • 2018-06-08
    • 2017-12-24
    相关资源
    最近更新 更多