2017-04-21 99 views
1

我是Dagger 2的新手,我正在寻找一种方法来获得“可配置组件”。匕首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 methods cannot be abstract遗憾的是无论是接口和抽象类。我也尝试过使用具体实现的非抽象基类,该实现返回null,然后在子类中重写这些类。但是这也会因@Provides methods may not override another method而失败。

总之我想定义一个模块的合约,并在运行期间选择不同的模块。我知道Dagger 2的编译时间验证了对象图,但是如果我有一个明确定义的合同,它仍然可能是正确的?还是我不得不为两个用户界面创建两个不同的组件,其代码重复?我还有其他解决方案吗?

回答

2

我不认为使用一个模块这种方式是可行的,因为...

假设你有以下两个构造你的类

@Inject ConsoleErrorReporter(Console console); 

@Inject DialogErrorReporter(Graphics graphics); 

这意味着ConsoleUIModule将需要ConsoleDialogErrorReporter将需要一个Graphics对象来创建其各自的实现ErrorReporter

但是,如果匕首只知道UIModule,因为您使用的界面在那里......呃......它无法提供任何一个的依赖关系,因为它不知道它们中的任何一个。

如果你不知道在编译时建立依赖关系图的依赖关系将不起作用。此外,即使没有匕首也不会编译,因为provideErrorReporter(ConsoleErrorReporter cer)不会覆盖provideErrorReporter()


你可以而且应该做的是使用不同的组件。因为组件是实际知道如何提供的东西。一个组件已经是一个接口—这就是你想要的,对吧?

您可以拥有组件依赖关系,其中一个组件依赖于另一个组件。例如。有一个DependentComponent,它提供了一个NeedsErrorReporter,需要执行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(); 
+0

我种了它的工作,当我用''通过编译javac'它gradle'编译......但当我用'm2apt'在eclipse中编译时出现错误(或者称为w/e),因为它不会为继承“接口组件”的组件之一生成Dagger组件...您是否遇到过这个问题? –

+0

@EmilyL。我从来没有在注释处理器上使用eclipse,所以我实在不知道。也许你在用eclipse使用不同的匕首版本?我使用了'2.10'。 –

+0

我在gradle中使用'apt'插件来生成eclipse文件。我证实这是同一把匕首'2.10'。我将不得不深入挖掘:/ –