我对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 methods cannot be abstract
失败了。我还尝试了非抽象基类,其具体实现返回null,然后在子类中覆盖它们。但是,这也会因@Provides methods may not override another method
而失败。
简而言之,我想为模块定义一个契约,并在运行时选择不同的模块。我知道Dagger 2编译时验证了对象图,但是如果我有一个明确定义的合同应该仍然可能吗?或者我是否被迫为两个用户界面创建两个具有重复代码的不同组件?我还缺少其他解决方案吗?
答案 0 :(得分:4)
我不认为以这种方式使用模块是可能的,因为......
假设您的类具有以下两个构造函数
@Inject ConsoleErrorReporter(Console console);
@Inject DialogErrorReporter(Graphics graphics);
这意味着ConsoleUIModule
需要Console
而DialogErrorReporter
需要Graphics
个对象来创建ErrorReporter
的重新实现。
但是如果匕首只知道UIModule
,因为你在那里使用界面......那么......它无法提供任何依赖关系,因为它不知道它们中的任何一个。 / p>
如果您不了解依赖关系,那么在编译时构建依赖关系图将无法正常工作。即使没有匕首也不会编译,因为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();