Dependency injection framework.
Dagger2
WHO?• 松村 勇輝
• Twitter. @Yuki_312
• Yukiの枝折. http://yuki312.blogspot.jp/
• Android Developer
CONTENTS• 依存性
• DI Framework
• Dagger2
• テストとアーキテクチャ
• 補足
依存性GitHubの情報を“保存する”アプリケーション
GitHub <interface>GitHubStore
依存性GitHubの情報を“保存する”アプリケーション
GitHubStoreを使って“保存”を実現する
GitHub <interface>GitHubStore
依存性
implements
インタフェースはそのままでは使えない.
具象化した実装クラスが必要.
GitHubDatabase
GitHub <interface>GitHubStore
依存性実装クラスはインスタンス化しなければならない.
GitHubDatabase
new
GitHub <interface>GitHubStore
依存性実装クラスはインスタンス化しなければならない.
class GitHub { private GitHubStore store = new GitHubDatabase();}
GitHubDatabase
new
GitHub <interface>GitHubStore
依存性newが依存性を生む.
GitHubの保存形式がDatabaseに固定されてしまう.
GitHubDatabase
new
GitHub <interface>GitHubStore
class GitHub { private GitHubStore store = new GitHubDatabase();}
依存性依存性はシステムの振る舞いを固定する.
GitHubDatabase
new
GitHub <interface>GitHubStore
class GitHub { private GitHubStore store = new GitHubDatabase();}
依存性依存性はシステムの振る舞いを固定する.
依存性はシステムの多様性を殺す.
GitHubDatabase
new
GitHub <interface>GitHubStore
class GitHub { private GitHubStore store = new GitHubDatabase();}
依存性依存性はテストの振る舞いを固定する.
依存性はテストの多様性を殺す.
テストが辛い.
DEPENDENCY INJECTION
• 制御の反転 – Inversion of Control
• ハリウッドの原則 – Hollywood Principle
依存性の注入
GitHub GitHubDatabasenew
ハリウッドの原則「おまえが呼ぶな, おれが呼ぶ」
依存性の注入
GitHub GitHubDatabasenew
依存性の注入
GitHub GitHubDatabaseInject
ハリウッドの原則「おまえが呼ぶな, おれが呼ぶ」
class GitHub { public GitHub(GitHubStore store) {...}}
new GitHub(new GitHubDatabase());
依存性の注入
GitHub GitHubDatabaseInject
ハリウッドの原則「おまえが呼ぶな, おれが呼ぶ」
class GitHub { public GitHub(GitHubStore store) {...}}
new GitHub(new GitHubDatabase());
依存性の注入
ハリウッドの原則「おまえが呼ぶな, おれが呼ぶ」
GitHub GitHubStore
GitHubDatabase
Client
new
Inject
class GitHub { public GitHub(GitHubStore store) {...}}
new GitHub(new GitHubDatabase());
依存性の注入GitHubの依存性を制御できるようになった.
e.g. 商用ではDatabase, 検証用ではMemory cacheに...
切り替えが容易になって, テストが楽になる!
new, new, new...制御の反転だけでは生成コードが散在する
new, new, new...制御の反転だけでは生成コードが散在する
ClientA: new GitHub(new GitHubDatabase());
ClientB: setDatabase(new GitHubDatabase());
ClientC: create(new GitHubDatabase());
new, new, new...e.g. UnitTestではMemory cacheでテストしたい
ClientA: new GitHub(new GitHubDatabase());
ClientB: setDatabase(new GitHubDatabase());
ClientC: create(new GitHubDatabase());
new, new, new...e.g. UnitTestではMemory cacheでテストしたい
ClientA: new GitHub(new GitHubDatabase() new GitHubMemcached());
ClientB: setDatabase(new GitHubDatabase() new GitHubMemcached());
ClientC: create(new GitHubDatabase() new GitHubMemcached());
new, new, new...クライアントも含めたテストが難しい...
システム全体の依存性を変更するのが難儀...
ClientA: new GitHub(new GitHubDatabase() new GitHubMemcached());
ClientB: setDatabase(new GitHubDatabase() new GitHubMemcached());
ClientC: create(new GitHubDatabase() new GitHubMemcached());
Factory Pattern
GitHub GitHubStore
GitHubDatabase
Client
Factory Pattern適用前
Factory Pattern
GitHub GitHubStore
GitHubDatabase
Client
new
Factory Pattern適用前
Factory Pattern
GitHub GitHubStore
GitHubDatabase
Client
InjectGitHubDatabase
Factory Pattern適用前
Factory Pattern
GitHub GitHubStore
GitHubDatabase
Client
InjectGitHubDatabase
Factory Pattern適用前
class GitHub { public GitHub(GitHubStore store) {...}}
new GitHub(new GitHubDatabase());
Factory PatternFactory Pattern適用後
GitHub GitHubStore
GitHubDatabase
Client
Factory Pattern生成処理をFactoryに委譲して問題領域を局所化
GitHub GitHubStore
GitHubDatabase
Client
Factory
Factory Pattern生成処理をFactoryに委譲して問題領域を局所化
GitHub GitHubStore
GitHubDatabase
Client
Factory
Get
Factory PatternFactoryがGitHubDatabaseを生成する
GitHub GitHubStore
GitHubDatabase
Client
Factorynew
Factory PatternFactoryがGitHubDatabaseを生成する
GitHub GitHubStore
GitHubDatabase
Client
Factory
Factory PatternClientを実装の詳細から隠蔽する
GitHub GitHubStore
GitHubDatabase
Client
Factory
Inject
Factory PatternClientを実装の詳細から隠蔽する
GitHub GitHubStore
GitHubDatabase
Client
Factory
new GitHub(Factory.getGitHubStore(type, flag));
Inject
Factoryの問題
• 多くの生成処理を委譲されて肥大化する
Factoryの問題
• 多くの生成処理を委譲されて肥大化する
• 生成順序, 構築方法に責任を持つ責務過多
Factoryの問題
• 多くの生成処理を委譲されて肥大化する
• 生成順序, 構築方法に責任を持つ責務過多
• Shared Object? Singleton?
Factoryの問題
• 多くの生成処理を委譲されて肥大化する
• 生成順序, 構築方法に責任を持つ責務過多
• Shared Object? Singleton?
• Lifecycle, Scopeの管理も必要
Factoryの問題
依然, 散在するコピペコード
Factoryの問題
依然, 散在するコピペコード
Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);Factory.getGitHubStore(type, flag);
DI Framework• コピペコードの排除
DI Framework• コピペコードの排除
• 依存オブジェクトの管理を委譲
DI Framework• コピペコードの排除
• 依存オブジェクトの管理を委譲
GitHub GitHubStore
GitHubDatabase
Client
Factorynew
Inject
依存オブジェクト
DI Framework• コピペコードの排除
• 依存オブジェクトの管理を委譲
GitHub GitHubStore
GitHubDatabase
Client
new
DI FW.
Inject
依存オブジェクト
Dagger2• for Java & Android
• Annotation Processingベースでデバッグが楽
• 高速
• コンパイル時に依存性の検証を行う
• Googleがメンテナ
Dagger2DI Frameworkの3ステップ
Dagger2DI Frameworkの3ステップ
1. 依存性の要求
Dagger2Request Dependency
GitHub
Dagger2DI Frameworkの3ステップ
1. 依存性の要求
2. 依存性の探索
Dagger2GitHub
Lookup Dependency
Dagger2DI Frameworkの3ステップ
1. 依存性の要求
2. 依存性の探索
3. 依存性の充足
Dagger2Inject Dependency
GitHub
依存性の要求
依存オブジェクトをどのように要求するのか?
Request Dependency
GitHub
依存性の要求
依存オブジェクトは @Inject で要求する
Dagger2
GitHub
GitHubStore
依存性の要求
依存オブジェクトは @Inject で要求する
Dagger2
GitHub
GitHubStore@Inject GitHubStore store;
依存性の要求
依存オブジェクトは @Inject で要求する
Dagger2
GitHub
GitHubStore
Request Dependency
@Inject GitHubStore store;
依存性の探索
依存性オブジェクトをどのように探索するのか?
Dagger2
Lookup Dependency
依存性の探索
Dagger2
GitHubDatabase
依存性の探索
Dagger2
GitHubDatabaseclass GitHubDatabase { @Inject GitHubDatabase() {...}}
依存性の探索
Dagger2
GitHubDatabase
lookup & new
class GitHubDatabase { @Inject GitHubDatabase() {...}}
依存性の探索
Dagger2
GitHubDatabase
GitHubDatabase
依存性の充足
依存性をどのように充足するのか
Dagger2Inject Dependency
GitHub
Dagger2GitHub
依存性の充足
ソフトウェア全体の依存性を満たす
GitHubStore GitHubDatabase
Dagger2GitHub
依存性の充足
ソフトウェア全体の依存性を満たす
GitHubStore GitHubDatabaseDI
Dagger2GitHub
依存性の充足
ソフトウェア全体の依存性を満たす
GitHubDatabase GitHubDatabase
Dagger2
依存性の充足
ソフトウェア全体の依存性を満たす
GitHub
GitHubWebApi
GitHubDatabase GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2GitHub
GitHubWebApi GitHubWebApi
GitHubDatabase GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2GitHub
GitHubWebApi
DIGitHubDatabase
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2GitHub
GitHubDatabase
GitHubWebApi GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2RepositoryViewer
GitHub
GitHubDatabase
GitHubWebApi
GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2RepositoryViewer
GitHub
GitHubDatabase
GitHubWebApi
GitHub
lookup & newGitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2
GitHub
RepositoryViewer
GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2DI
RepositoryViewer
GitHub GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2RepositoryViewer
GitHub GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2RepositoryViewer
GitHub GitHublookup & new
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2
RepositoryViewer
GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
ソフトウェア全体の依存性を満たす
Dagger2
GitHubDatabaseGitHubWebApi
GitHub
RepositoryViewer RepositoryViewer
GitHub
GitHubWebApi
GitHubDatabase
依存性の充足
Graph
GitHubDatabaseGitHubWebApi
GitHub
RepositoryViewer
依存性の探索
コンストラクタへの@Injectでは解決できないケース
依存性の探索
コンストラクタへの@Injectでは解決できないケース
• インタフェースの解決(具体化)
• 管理外クラスへの@Inject宣言
• オブジェクトの構築を伴う生成
PROVIDE DEPENDENCY
依存性の提供
• Dagger2に閉じて解決できない
特殊な依存性を解決するFactory methodの提供
依存性の提供
Instance Provider
provideGitHubStore
依存性の提供
@Provides
Instance Provider
provideGitHubStore
依存性の提供
@Provides
Instance Provider
provideGitHubStore
@Provides GitHubStore provideGitHubStore() { return new GitHubDatabase();}
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStoreGitHubDatabase
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStoreGitHubDatabase
RequestDependency
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStore
Resolved Dependency
GitHubDatabase
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStore
Call Factory Method
GitHubDatabase
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStore
newGitHubDatabase
依存性の提供
Dagger2GitHub
GitHubStore
Instance Provider
provideGitHubStore
GitHubDatabase
GitHubDatabase
依存性の提供
Dagger2GitHub
GitHubStore
DI
GitHubDatabase
Instance Provider
provideGitHubStoreGitHubDatabase
依存性の提供
GitHub
GitHubDatabase
Dagger2
GitHubDatabase
Instance Provider
provideGitHubStoreGitHubDatabase
依存性の提供
@Provides
Instance Provider
provideGitHubStore
@Provides GitHubStore provideGitHubStore() { return new GitHubDatabase();}
依存性の提供
@Provides
• インタフェースの解決(具体化)
• 管理外クラスへの@Inject宣言
• オブジェクトの構築を伴う生成
Instance Provider
provideGitHubStore
@Provides GitHubStore provideGitHubStore() { return new GitHubDatabase();}
依存性の提供
@Providesを持つクラスには @Module を付与する
@Moduleclass ApplicationModule { @Provides GitHubStore provideGitHubStore() { return new GitHubDatabase();
}
}
Graph
Building the GraphGraphは “Component” の単位で管理する
GitHubDatabaseGitHubWebApi
GitHub
RepositoryViewer
Dagger2
Component
Building the GraphGraphの設計図として@Componentを宣言する
@Component(modules=ApplicationModule.class)interface ApplicationComponent {...}
Building the GraphGraphの操作はComponentを通じて行われる
Client Component
ApplicationComponent component = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build();
@InjectmemberField;
@Module
@Component / Dagger2
@Provide
Interface/ Unmanaged Class
Graph
CREATE / BUILD
PROVIDEFACTORY METHOD
DEPENDENCYINJECTION
@InjectConstructor()
CREATE
依存オブジェクト
依存性の充足
依存性の要求
TEST & ARCHITECTURE
Test & ArchitectureDIが促進するもの
• レイヤーをきれいに分離できる
• レイヤーが独立し, レイヤーの差し替えが容易
• テストが楽になる!
Test & Architectureテスタビリティの向上
• テストは検証用モジュールで実施
• テスト用レイヤに差し替え etc.
Demo商用…WebAPIの結果を表示&Databaseへ永続化
検証用…WebAPIの結果を表示&オンメモリキャッシュ
シナリオ:
通常は商用環境で試験.
ただし, AWS Device Farmでは検証用で試験.
Demo商用モジュールで実施
Dagger2
GitHubDatabase
GitHubWebApi
GitHub
RepositoryViewer
GitHubStore
Dagger2
Demo商用モジュールで実施
DIGitHubWebApi
GitHub
RepositoryViewer
GitHubStore
GitHubDatabase
Demo商用モジュールで実施
GitHubWebApi
GitHub
RepositoryViewer
GitHubDatabase
Dagger2
GitHubDatabase
Demo検証用モジュールで実施
Dagger2
GitHubMemcacheGitHubWebApi
GitHub
RepositoryViewer
GitHubStore
Demo検証用モジュールで実施
Dagger2
GitHubMemcache
DIGitHubWebApi
GitHub
RepositoryViewer
GitHubStore
Demo
Dagger2
GitHubMemcacheGitHubWebApi
GitHub
RepositoryViewer
GitHubMemcache
検証用モジュールで実施
Sample codeGitHub - Dagger2Sample
https://github.com/YukiMatsumura/Dagger2Sample
Dependency Injection• オブジェクトの生成は依存性を生む
• オブジェクトの生成はコスト/責務である
• おまえが呼ぶな, おれが呼ぶ
• DIがレイヤーの独立性を高める
• DIがテスタビリティを高める
Graphの操作
ComponentでGraphの操作を定義
• Graphが属するScopeの宣言
• 依存性注入のポイントを外部公開
• 他Componentへの依存
Scope依存オブジェクトのライフサイクルを指定
• Application単位のSingleton性を持たせる
• Activity単位のSingleton性を持たせる etc.
Qualifier• 依存性の注入先に識別子を付ける
• 同じ型の依存性解決に使用される
Lazy Injection• オブジェクト取得時に依存性を解決・注入する
• 遅延初期化
Provider Injection• 依存性注入の都度newするnon-cached指定
Subcomponent• ComponentAとComponentBに親子関係を持たせる
• ComponentA+ComponentBのGraphをつくる
dependencies• ComponentAとComponentBに使用関係を持たせる
• ComponentA+ComponentBのGraphをつくる
以上
ご清聴ありがとうございました