1. matsurih

    Posted

    matsurih
Changes in title
+DIの基本とAndroidでのDI利用パターンについて
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,242 @@
+この記事は [AdventCalendar Android その2](https://qiita.com/advent-calendar/2017/android_second) の9日目の記事です。
+
+---
+
+Dependency Injection(以下、DI)についてのお話をします。
+
+細かい話はいろいろな所で既にたくさんありますので、
+「とりあえずAndroidでDIを取り入れたい」という方向けに簡単にご説明しようと思います。
+厳密には正しくない話も紛れていますがご了承下さい。
+
+#DIとは?
+
+英語でDependency Injection、よく和訳で「依存性(の)注入」とか言われるデザインパターンです。
+「依存性を注入する」というと何やらヤバげな匂いがしますが、
+「依存性」とは、もうすこし簡単に言うと「(依存関係にある)オブジェクト」と言い換えられます。
+つまり「依存関係にあるオブジェクトは外部から注入するようなデザインパターン」です。
+
+もう少し具体的にご説明しようと思います。
+
+```kotlin:Sample1.kt
+
+class Main{
+ Client().methodB()
+}
+
+class Client{
+ fun methodB(){
+ val service: Service = Service() // Serviceを生成
+ service.methodA() // Serviceを利用
+ }
+}
+
+class Service{
+ fun methodA(){
+ ...
+ }
+}
+
+```
+
+上記の例はDIパターンを用いていません。
+Clientクラスの中でServiceオブジェクトを生成し、methodAを呼び出しています。
+これを、「ClientクラスはServiceクラスに依存している」とか言ったりします。
+
+実際にはこれでもきちんと動作しますし、このような記法はいたるところで頻繁に利用されています。
+
+```kotlin
+
+class Main{
+ val service = Service() // Serviceを生成
+ val client = Client(service) // Serviceを注入
+ client.methodB()
+}
+
+class Client(s: Service){
+ val service = s
+ fun methodB(){
+ service.methodA() // Serviceを利用
+ }
+}
+
+class Service{
+ fun methodA(){
+
+ }
+}
+
+```
+
+あとは、DIパターンにかぎらず、オブジェクト間の結合をより疎にする手法として「抽象に依存せよ」という言葉があります。
+いままではクラス名を直接指定してオブジェクトを生成していましたが、
+これだとスタブを利用して実装を行うときやテスト用のモックを利用したい時に、
+Clientクラスのコンストラクタの型を書き換えてあげないといけません。
+そうなるとテストももちろん大変になりますが、リリース時に直し忘れていたりすると・・・更に大変ですね。
+
+ここでいう「抽象」とは、インターフェースのことを指すと思っていただいてOKです。
+今回はIServiceインターフェースを作成し、ServiceクラスにIServiceクラスを実装させるようにします。
+そして、今まで具体的にServiceのクラス名を明示していたClientクラスのコンストラクタをIServiceに書き換えます。
+
+
+
+```kotlin
+
+val test= true
+
+class Main{
+ val service = if(!test) Service() else StubService() // ServiceA or Bを生成
+ val client = Client(service) // ServiceA or Bを注入
+ client.methodZ()
+}
+
+class Client(s: IService){
+ val service = s
+ fun methodZ(){
+ service.methodX() // ServiceA or Bを利用
+ }
+}
+
+interface IService{
+ fun methodX()
+}
+
+class Service: IService{
+ fun methodX(){
+ ...
+ }
+}
+
+class StubService: IService{
+ fun method X(){
+ ...
+ }
+}
+
+```
+
+こうなると、例えばServiceクラスの実装がまだでも、とりあえずStubServiceを実装しておけば以後の開発が進められますね。
+また、下流のテストを実施したい場合も同様です。
+
+まとめてしまうと、
+
+- オブジェクトの「生成&注入」と「利用」を分離せよ
+- 抽象(インターフェース)に依存せよ
+
+この2点を意識するようにすれば、それなりにDIっぽいものは書けているはずです。
+今回はコンストラクタDIというものを利用しましたが、他のパターンもあります。興味が湧いたら調べて見て下さい。
+
+
+# AndroidでのDI利用パターン
+
+さて、DIの説明だけでだいぶ尺を取ってしまいましたが、今回はAndroid Advent Calenderということで、
+AndroidでのDI利用パターンについてもお話しないといけませんね。
+
+上記で触れたような簡易的なDIデザインパターンで小規模なコードであれば必ずしも必要ではないのですが、
+DI実装を楽にするためにDIコンテナというものを利用することができます。
+(どんどん実装が大きくなると、上記のコンストラクタDIの場合、引数の数がどんどん増えてしまいますよね)
+
+DIコンテナを利用することで、引数の数や種類が変わったりしても元のソースコードではなく、
+DIコンテナ側を修正することで対応できるようになり、保守性が上がります。
+
+Androidで利用可能なDIコンテナ(ライブラリ)として有名なのは以下のようなものでしょうか。
+
+- [Dagger](https://google.github.io/dagger/)
+- [Proton](https://github.com/hnakagawa/proton)
+- [RoboGuice](https://github.com/roboguice/roboguice)
+
+しかし上記の3つはいずれも `javax.inject.Inject` アノテーションを利用します。
+
+・・・が、私はアノテーションが嫌いです。
+
+特にKotlin + Daggerでプロジェクトを始めようとしてググった結果最初に出てくるような、
+ウェブサイトの言う通りに実装すると間違いなくDIライブラリのアノテーション絡みのコード自動生成がうまくいきません。
+
+なので私は
+
+- [Kodein](https://github.com/SalomonBrys/Kodein)
+
+というDIコンテナをよく利用します。
+
+## Kodein
+
+Kodeinを使うメリットとして公式には以下の通りの記載があります。
+
+> - JVM上/Android上/javascript上で利用可能
+> - 必要になった時に依存性の遅延読み込みを行う
+> - オブジェクトの初期化の順序を気にする必要がない
+> - クラス/インターフェースをインスタンス/プロバイダに容易にバインドできる
+> - オブジェクトのバインドや再帰を容易にデバッグできる
+> - 軽量で高速に動作し、最適化も行われている
+> - 可読性の高い宣言的プログラミングを実現することができるよう設計されている
+> - Androidとの統合性が高い
+> - Kotlin風の文法で利用可能なAPIを提供している
+> - もちろん普通にJavaでも利用可能
+
+そして何より、`javax.inject.Inject`を始めとするアノテーションプロセッシングを利用しないため、私のようなアノテーション嫌いにも最適です。
+
+### インストール
+
+簡単なのはGradleかMavenで以下の通りです。
+
+```xml:Maven
+<dependency>
+ <groupId>com.github.salomonbrys.kodein</groupId>
+ <artifactId>kodein</artifactId>
+ <version>4.1.0</version>
+</dependency>
+```
+
+```groovy:Gradle
+compile 'com.github.salomonbrys.kodein:kodein:4.1.0'
+```
+
+### 依存性の宣言
+
+たとえば、文字列をインスタンスに注入するために、ここでバインドしてあげます。
+どこでもいいのですが、とりあえずどこでも簡単に拾えるようにApplicationクラスのサブクラスとしてみましょう。
+
+```kotlin:KodeinTest.kt
+class MyApp: Application(), KodeinAware {
+ override val kodein = Kodein {
+ bind<String>() with instance("This is binded string.")
+ }
+}
+```
+
+### 依存性解決
+
+```kotlin:MainActivity.kt
+class MainActivity : KodeinAppCompatActivity() {
+ val message: String by instance()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Log.d(message)
+ }
+}
+```
+
+この `val message: String by instance()` により、message変数に文字列が注入されます。
+
+### 何が嬉しい?
+上記の例だと1つしか依存性の注入を行っていないのでわかりにくいのですが、
+1つのKodeinブロックの中で複数の依存性の宣言を行うことももちろん可能です。
+その場合、どの順番で宣言や注入を行うかを考える必要がなく、
+`with XXXという記法に従いKodeinライブラリが自動で決定してくれます。
+
+また、ここに依存性を追加したいと思った時に、例えばコンストラクタDIを手で実装すると、
+コンストラクタに引数を追加して、その受け渡し部分を修正して、とやや手間がかかりますが、
+Kodeinを利用すると `Kodein{ }` 内に依存性の宣言を1つ追加し、
+利用したいところでは`by XXX()` で依存性を注入することができるようになります。
+
+逆に、注入する必要のない依存性については、その注入を省くこともできるため、
+Service側の実装にあわせて、そのServiceに依存する全てのClientを修正する必要もなくなります。
+
+#まとめ
+DIは結合度を低下させ、アジャイル開発や単体テストに対し強いメリットを持っています。
+小難しい用語や定義は抜きにして、まずは「それっぽいもの」を1つ作ってみると、
+だんだんと何が便利なのかがわかってくるのではないかなと思います。
+
+ぜひAndroid開発にもDIを活用してみて下さい!
+
+