web-dev-qa-db-ja.com

短剣2:実行時に提供された依存関係を変更する方法

Dagger 2を学ぶために、アプリケーションを書き直すことにしましたが、次の問題の適切な解決策を見つけるのに苦労しています。

この例では、Modeというインターフェイスがあると仮定します。

_public interface Mode {
    Object1 obj1();

    //some other methods providing objects for app
}
_

および2つの実装:NormalModeおよびDemoMode

モードはシングルトンで保存されるため、アプリケーション内のどこからでもアクセスできます。

_public enum ModeManager {
  INSTANCE,;

  private Mode mode;

  public Mode mode() {
    if (mode == null)
      mode = new NormalMode();
    return mode;
  }

  public void mode(Mode mode) { //to switch modules at runtime
    this.mode = mode;
  }
}
_

NormalModeは実行時にDemoModeに切り替えられます(たとえば、ユーザーがバックグラウンドで数回クリックした場合)

_public void backgroundClicked5Times(){
  ModeManager.INSTANCE.mode(new DemoMode());
  //from now on every object that uses Mode will get Demo implementations, great!
}
_

そこで、最初にシングルトンを取り除き、モードをDagger2モジュールとして定義しました。

_@Module
public class NormalModeModule {
  @Provides
  public Object1 provideObject1() {
    return new NormalObject1();
  }
}

@Module
public class DemoModeModule {
  @Provides
  public Object1 provideObject1() {
    return new DemoObject1();
  }
}
_

ここで、メソッド_backgroundClicked5Times_で、シングルトンを処理する代わりに、DAGでNormalModeModuleDemoModeModuleに置き換えて、_Object1_を必要とする他のクラスが_DemoObject1_これからの実装。

Daggerでそれを行うにはどうすればよいですか?

前もって感謝します。

13
Marcin

しばらくの間短剣を試してみて、私のユースケースでうまく機能しているように見える解決策を思いつきました。

  1. モードに関する状態情報を保持するクラスを定義します

    public class Conf {
      public Mode mode;
    
      public Conf(Mode mode) {
        this.mode = mode;
      }
    
      public enum Mode {
        NORMAL, DEMO
      }
    }
    
  2. モジュールでConfのシングルトンインスタンスを提供します

    @Module
    public class ConfModule {
      @Provides
      @Singleton
      Conf provideConf() {
        return new Conf(Conf.Mode.NORMAL);
      }
    }
    
  3. AppComponentにモジュールを追加します

    @Singleton
    @Component(modules = {AppModule.class, ConfModule.class})
    public interface AppComponent {
        //...
    }
    
  4. モードに基づいて異なるオブジェクトを提供するモジュールを定義する

    @Module
    public class Object1Module {
    
      @Provides
      Object1 provideObject1(Conf conf) {
        if (conf.mode == Conf.Mode.NORMAL)
          return new NormalObject1();
        else
          return new DemoObject1();
      }
    }
    
  5. 実行時にモードを切り替えるには、Confオブジェクトを挿入して変更するだけです。

    public class MyActivity extends Activity {
        @Inject Conf conf;
    
        //...
    
        public void backgroundClicked5Times(){
            conf.mode = Conf.Mode.DEMO;
    
            //if you have dagger objects in this class that depend on Mode
            //execute inject() once more to refresh them
        }
    }
    
3
Marcin

たぶん、マルチバインディングの使用を検討できますか?

@Module
public class NormalModeModule {
  @Provides
  @IntoMap
  @StringKey("normal")
  public Object1 provideObject1() {
    return new NormalObject1();
  }
}

@Module
public class DemoModeModule {
  @Provides
  @IntoMap
  @StringKey("demo")
  public Object1 provideObject1() {
    return new DemoObject1();
  }
}

モードを使用する場合:

@Inject
Map<String, Mode> modes;
//or you perfer lazy initialization:
Map<String, Provider<Mode>> modes;

public void backgroundClicked5Times(){
  ModeManager.INSTANCE.mode(modes.get("demo"));
  //if you are using Provider:
  ModeManager.INSTANCE.mode(modes.get("demo").get());
  //from now on every object that uses Mode will get Demo implementations, great!
}
0
ZumiKua