俺的 Dagger ベストプラクティス

# Dagger

[Dagger](http://hajimes.github.io/dagger/) は Android/Java で動作する依存性注入(Dependency Injection)ライブラリです。
結構 Dagger を使っているプロジェクトはあるのですが、設計やその利用方法に疑問点や改善点が散見されるので、まとめておこうと思います。

[Dagger 2](http://google.github.io/dagger/)がリリースされていますが、``@Module``Annotation 内に記述していた、依存関係が``@Component``に移譲された認識ですので、考え方は同じはずです。

## Dagger は画面(or 機能)単位の依存関係を記述するべき

例えばログイン画面、リスト画面、詳細画面の場合の記述

### :umbrella: Bad Pattern

```
[root: ApplicationModule] >[injects: Application.java]
　└──<[includes: AwesomeModule] 
　　　　　└──>[injects: LoginActivity.java]
　　　　　└──>[injects: ListActivity.java]
　　　　　└──>[injects: DetailActivity.java]
```

```java
@Module(
  includes = AwesomeModule.class,
  library = true
)
public class ApplicationModule {
  ...
}
```

```java
@Module(
  injects = {
    LoginActivity.class,
    ListActivity.class,
    DetailActivity.class
  },
  library = true
)
public class AwesomeModule {
  ...
}
```

これでは子の依存関係を作成した場合に不要な ``@Inject`` を行ってしまいます。

### :sunny: Good Pattern

> ![Dagger graph](http://frogermcs.github.io/images/12/dagger1-graph.jpg)
> 
> See : http://frogermcs.github.io/dagger-1-to-2-migration/

```
[root: ApplicationModule] >[injects: Application.java]
　├──<[addsTo: LoginModule] 
　│　　　└──>[injects: LoginActivity.java]
　├──<[addsTo: ListModule]
　│　　　└──>[injects: ListActivity.java]
　└──<[addsTo: DetailModule]
　　　　　└──>[injects: DetailActivity.java]
```

```java
@Module(
  includes = AwesomeModule.class,
  library = true
)
public class ApplicationModule {
  ...
}
```

```java
@Module(
  injects = LoginActivity.class,         
  addsTo = ApplicationModule.class,
  library = true
)
public class LoginModule {
  ...
}
```

```java
@Module(
  injects = ListActivity.class,         
  addsTo = ApplicationModule.class,
  library = true
)
public class ListModule {
  ...
}
```

```java
@Module(
  injects = DetailActivity.class,         
  addsTo = ApplicationModule.class,
  library = true
)
public class DetailModule {
  ...
}
```

このように ``addsTo`` で子の依存関係として追加することで画面(or 機能)毎の注入を選択できるようになります。

## Dagger は ``@Inject`` 指定された場合、インスタンス化が可能なものはインスタンス化する事を知っておくべき

例えば以下の様な構成の場合

```java
// Retrofit interface
interface YourService {
  getVideo(int id);
  getUser(int id);
}
```

```java
public class final VideoDao {
  final YourService mService;

  public VideoDao(YourService service) { mService = service; }

  public Observable<VideoModel> get(int id) {
    return service.getVideo(id).first();
  }
}

public class final UserDao {
  final YourService mService;

  public UserDao(YourService service) { mService = service; }

  public Observable<VideoModel> get(int id) {
    return service.getUser(id).first();
  }
}
```

```java
@Module(complete = false, library = true)
public class ApiModule.java {
  @Provides @Singleton
  public VideoDao provideYourService(RestAdapter adapter) { ... }
}
```

```java
public class AwesomeActivity extends Activity {
  @Inject
  VideoDao mVideoDao;

  @Inject
  UserDao mUserDao;

  ...
}
```

### :umbrella: Bad Pattern

```java
@Module(
  injects = AwesomeActivity.class,
  includes = ApiModule.class
)
public class DaoModule {
  @Provides @Singleton
  public VideoDao provideVideoDao(YourService service) { ... }

  @Provides @Singleton
  public VideoDao provideUserDao(YourService service) { ... }
}
```

この場合 ``YourService`` が ``@Provides`` されていれば DAO を``@Provides`` する必要ありません。

### :sunny: Good Pattern

```diff
+ @Singleton
public class final VideoDao {
  final YourService mService;

+ @Inject
  public VideoDao(YourService service) { mService = service; }

  public Observable<VideoModel> get(int id) {
    return service.getVideo(id).first();
  }
}

+ @Singleton
public class final UserDao {
  final YourService mService;

+ @Inject
  public UserDao(YourService service) { mService = service; }

  public Observable<UserModel> get(int id) {
    return service.getUser(id).first();
  }
}
```

```java
// Application Module
@Module(
  includes = ApiModule.class,
  injects = YourApplication.class
)
public class ApplicationModule {
  ...
}
```

```java
// Awesome domain Module
@Module(
  injects = AwesomeActivity.class,
  addsTo = ApplicationModule.class,
  library = true
)
public class AwesomeModule {
}
```

上記のように Annotation と依存関係を追加すれば、DAO を ``@Provides`` する必要がありません。

## Dagger で複数インスタンス作成される場合は ``Lazy<T>`` を利用すべき

__Injection の種類__

* SINGLETON 
  * 唯一のインスタンスです。多くの他のオブジェクトから参照されます
* LAZY 
  * 一度生成されると破棄されるまで利用されるインスタンス。
* PROVIDER
  * 都度生成されるインスタンス。

``Provider<T>`` は便利ですが、自分でインスタンスの管理を行わなければならず混乱を招きます。
これが必要な場合、設計の見直しが必要かもしれません。

### :umbrella: Bad Pattern

```java
class MainPresenter {
  @Inject Provider<ChildPresenter> mChildProvider;

  private final List<ChildPresenter> mChilds = new ArrayList<>();

  public void OnViewInflated() {
    // Has 5 childs
    for (int i=0; i < childCount; i++) {
      final ChildPresenter presenter = mChildProvider.get();
      ((ChildView) getView().getChildAt(i)).setPresenter(presenter);
      mChilds.add(presenter);
    }    
  }

  public void fireChildEvent(int id) {
     mChilds.get(id).fire();
  }
}

class ChildPresenter {
  public void fire() { ... }
}
```

```java
class ChildView extend View {
  private ChildPresenter mPresenter;

  public void setPresenter(ChildPresenter presenter) { mPresenter = presenter; }
}
```

複数のネストした ``View`` が存在する場合は ``Provider<T>`` でインスタンスを作成するべきではありません。

### :sunny: Good Pattern

```java
class MainPresenter {　
  public void fireChildEvent(int id) {
     ((ChildView) getView().getChildAt(id)).getPresenter().fire();
  }
}

class ChildPresenter {
  public void fire() { ... }
}
```

```java
class ChildView extend View {
  @Inject
  Lazy<ChildPresenter> mPresenter;

  public ChildPresenter getPresenter() { return mPresenter.get(); }
}
```

``Lazy<T>`` を使うことで、インスタンスの管理から解放されます。

## Dagger でネストした CustomView へ ``ObjectGraph`` を渡すときは ``LayoutInflator.Factory`` を使うべき

Fragment 等から CustomView へ依存関係を引き渡す必要がある場合があると思いますが、その場合は ``Factory`` を作りましょう。
Jake もいってる https://github.com/square/dagger/issues/382
[Mortar](https://github.com/square/mortar) を使えば考えなくていいんですがね…。

### :umbrella: Bad Pattern

```java
public class AwesomeFragment extends Fragment {
  final ObjectGraph mObjectGraph;

  @Override
  public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mObjectGraph = MainApplication.get(getActivity()).getObjectGraph()
          .plus(getDaggerModule());
        mObjectGraph.inject(this);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.awesome_fragment,
      container, false);
    mObjectGraph.inject(view.findViewById(R.layout.awesome_view));
    return view;
  }

  ...
}
```

これだと ``Fragment`` と CustomView が ``ObjectGraph`` の状態によって密結合してしまうので、避けたほうがいいでしょう。

### :sunny: Good Pattern

```java
public class AwesomeView extends FrameLayout {
  static public class Factory implements LayoutInflater.Factory {
    final ObjectGraph mObjectGraph;

    public Factory(ObjectGraph objectGraph) { mObjectGraph = objectGraph; }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
    // null を返すと標準のファクトリを使うので、型名で判定する。
    if (!name.equalsIgnoreCase(AwesomeView.class.getName())) {
      return null;
    }

    return new AwesomeView(context, attrs, mObjectGraph);
  }

  public AwesomeView(Context context, AttributeSet attrs, ObjectGraph objectGraph) {
    objectGraph.inject(this);
  }

  ...
}
```

```java
public class AwesomeFragment extends Fragment {
  final ObjectGraph mObjectGraph;

  @Override
  public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mObjectGraph = MainApplication.get(getActivity()).getObjectGraph()
          .plus(getDaggerModule());
        mObjectGraph.inject(this);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    final LayoutInflater daggerInflater = inflater.cloneInContext(getActivity);
    daggerInflater.setFactory(new AwesomeView.Factory(mObjectGraph));

    final View view = inflater.inflate(R.layout.awesome_fragment,
      container, false);
    return view;
  }

  ...
}
```

CustomView 内で ``inject`` 操作を行ったほうが関係性を疎にできます。

## 参考
https://github.com/square/dagger
https://github.com/JakeWharton/u2020
https://github.com/LiveTyping/u2020-mvp