Dagger 2模块“interfaces”是什么?

4
我是一位新手,对Dagger 2不太熟悉,现在我正在寻找一种实现“可配置组件”的方法。
基本上,我想要实现以下内容:
public interface ErrorReporter{
   ...
}

public class ConsoleErrorReporter implements ErrorReporter{
   ... // Print to System.err
}

public class DialogErrorReporter implements ErrorReporter{
   ... // Show modal dialog to user
}


@Module
public interface UIModule{
    @Provides
    ErrorReporter provideErrorReporter();
}

@Module
public class ConsoleUIModule{
    @Override
    @Provides
    ErrorReporter provideErrorReporter(ConsoleErrorReporter cer){
        return cer;
    }
}


@Module
public class GraphicalUIModule{
    @Override
    @Provides
    ErrorReporter provideErrorReporter(DialogErrorReporter  der){
        return der;
    }
}

@Component(modules = {UIModule.class, OtherUniversalModule.class})
public interface ApplicationComponent{
    ErrorReporter errorReporter();
}


void main(String[] args){
    final UIModule uiModule;
    if(args.length == 1 && args[0].equals("gui")){
        uiModule = new GraphicalUIModule();
    }else{
        uiModule = new ConsoleUIModule();
    }

    DaggerApplicationComponentdac = DaggerApplicationComponent.builder()
                                       .uiModule(uiModule).build();
    dac.errorReporter().showError("Hello world!");
}

以上代码无论是针对接口还是抽象类都会出现@Provides方法不能为抽象的问题。我已经试过在非抽象基类中实现返回null的具体实现,然后在子类中重写这些实现,但是这也会出现@Provides方法不能覆盖其他方法的问题。
简而言之,我想要为一个模块定义一个契约,并在运行时选择不同的模块。我知道Dagger 2在编译时验证对象图,但如果我有一个定义良好的合约,那么这应该仍然是可能的,对吗?或者我是否被迫为两个用户界面创建具有重复代码的两个不同组件?我是否遗漏了其他解决方案?
1个回答

5
我认为以这种方式使用模块是不可能的,因为...
假设你有以下两个类的构造函数:
@Inject ConsoleErrorReporter(Console console);

@Inject DialogErrorReporter(Graphics graphics);

这意味着 ConsoleUIModule 需要一个 Console,而 DialogErrorReporter 需要一个 Graphics 对象来创建它们各自的 ErrorReporter 实现。
但是,如果 Dagger 只知道在 UIModule 中使用了接口……那么它将无法为其中任何一个提供依赖项,因为它不知道它们中的任何一个。
如果您不知道这些依赖关系,编译时构建依赖图也无法正常工作。即使没有 Dagger,这段代码也无法编译,因为 provideErrorReporter(ConsoleErrorReporter cer) 没有覆盖 provideErrorReporter()
你可以并且应该使用不同的组件。因为组件实际上就是知道如何提供东西的东西。而且组件已经是一个接口——这不是你想要的吗?
您可以具有组件依赖关系,其中一个组件依赖于另一个组件。例如,有一个提供 NeedsErrorReporterDependentComponent,需要一个 ErrorReporter 实现。我们还依赖于一个接口,而不是实际的组件(这毕竟是您想要的,对吧?)
然后,您通过实际组件实现接口,每个组件都有其各自的模块(甚至可能还有更多依赖项)。最终,您将拥有一个组件,可以切换并提供不同版本的对象,且被正确地封装!
@Component(dependencies = UIComponent.class) /* <- an interface! */
interface DependentComponent {
  NeedsErrorReporter needsErrorReporter();
}

class NeedsErrorReporter {
  @Inject public NeedsErrorReporter(ErrorReporter reporter) { }
}


/* this is _not_ a component, but a simple interface! */
interface UIComponent {
  ErrorReporter errorReporter();
}


/* Console */
@Component(modules = ConsoleUIModule.class)
interface ConsoleUIComponent extends UIComponent { }

@Module interface ConsoleUIModule {
  @Binds ErrorReporter provideErrorReporter(ConsoleErrorReporter cer);
}

/* Graphic */
@Component(modules = GraphicalUIModule.class)
interface GraphicUIComponent extends UIComponent { }

@Module interface GraphicalUIModule {
  @Binds ErrorReporter provideErrorReporter(DialogErrorReporter der);
}

/* The error reporter variants */
interface ErrorReporter {
}

class ConsoleErrorReporter implements ErrorReporter {
  @Inject public ConsoleErrorReporter() { }
}

class DialogErrorReporter implements ErrorReporter {
  @Inject public DialogErrorReporter() { }
}

现在你需要做的就是选择合适的组件 ;)
DaggerDependentComponent.builder().uIComponent(DaggerConsoleUIComponent.create()).build();
    // or
DaggerDependentComponent.builder().uIComponent(DaggerGraphicUIComponent.create()).build();

我有点让它工作了,当我通过gradlejavac编译时,它可以编译...但是当我使用m2apt在eclipse中编译时,我会遇到错误(或者叫其他名字)由于某种原因,它不会为继承“接口组件”的一个组件生成dagger组件...你遇到过这种情况吗? - Emily L.
@EmilyL。我从未在注解处理器中使用过Eclipse,所以我真的不知道。也许你正在使用与Eclipse不同的Dagger版本?我使用的是2.10 - David Medenjak
我正在使用gradle中的apt插件来生成eclipse文件。并且我已验证它与Dagger 2.10相同。我将不得不深入挖掘 :/ - Emily L.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接