2017-04-21 101 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'。我將不得不深入挖掘:/ –