DIの基本についての雑多メモ

DIについて整理してみた。
DIの原則を理解するために、依存関係逆転の法則を知らないといけない。

## 1. 依存関係逆転の法則

引用) https://www.slideshare.net/ShintaroKurosawa/20160526-62511723

DIを理解するために、「依存関係逆転の法則」について知っておく必要が前提としてある。
「依存関係逆転の法則」とは、以下のようなもの。

* 上位モジュールは下位モジュールに依存しない
* 上位も下位も抽象に依存すべき
* 抽象は実装の詳細に依存しない
* 実装の詳細が抽象に依存すべき

よくわからないのでJavaコード書いて以下に整理していく。

### 1.1. 初期状態

```java
import java.time.LocalDate;

public class Sample {
    public static void main(String[] args) {
        User user = new User(LocalDate.now());
        user.save();
    }
}

// ロジック層
class User {
    private LocalDate birthDay;

    User(LocalDate birthDay){
        this.birthDay = birthDay;
    }

    public int age() {
        return 18;
    }

    public LocalDate getBirthDay() {
        return birthDay;
    }

    public void save() {
        // UserRepositoryに依存している。
        UserRepository repository = new UserRepository();
        repository.save(this);
    }

}

// データアクセス層
class UserRepository {
    public void save(User user) {
        // insert Database
    }
}
```

上位モジュールのUserが下位モジュールのUserRepositoryに依存している。
図で書くと、以下のような感じ。

![図1.png](https://qiita-image-store.s3.amazonaws.com/0/203061/c78facda-204d-1b20-a77b-391422d0af12.png)


### 1.2. 上位も下位も抽象に依存するようにトライ。

```java
import java.time.LocalDate;

public class Sample {
    public static void main(String[] args) {
        User user = new User(LocalDate.now());
        user.save();
    }
}

// ロジック層
class User {
    private LocalDate birthDay;

    User(LocalDate birthDay) {
        this.birthDay = birthDay;
    }

    public int age() {
        return 18;
    }

    public LocalDate getBirthDay() {
        return birthDay;
    }

    public void save() {
        // 結局UserRepositoryの実装クラスのUserRepositoryImplに依存してしまっている。
        UserRepository repository = new UserRepositoryImpl();
        repository.save(this);
    }

}

// データアクセス層 抽象化
interface UserRepository {
    public void save(User user);
}

// データアクセス層 実装クラス
class UserRepositoryImpl implements UserRepository {
    @Override
    public void save(User user) {
        // insert Database
    }
}
```

But..
ロジック層とデータアクセス層の間に抽象のUserRepositoryインタフェースを入れたものの、
Userクラスは結局、実装クラスのUserRepositoryImplに依存してしまっている。。


![図2.png](https://qiita-image-store.s3.amazonaws.com/0/203061/25b1c237-bf2e-0aa2-1f9c-3961fb1f5115.png)


ここからDIする。

### 1.3. 依存性の注入(Dependency Injection)

上位も下位も抽象に依存するように再度トライする。

```java
// ロジック層
class User {
    private UserRepository repository; // フィールド変数にしてみた。
    private LocalDate birthDay;

    // コンストラクタで実装クラスのオブジェクトを受け取る。
    User(LocalDate birthDay, UserRepository repository) {
        this.birthDay = birthDay;
        this.repository = repository; // フィールド変数にセット。
    }

    public int age() {
        return 18;
    }

    public LocalDate getBirthDay() {
        return birthDay;
    }

    public void save() {
        repository.save(this);
    }

}

// データアクセス層 抽象化
interface UserRepository {
    public void save(User user);
}

// データアクセス層 実装クラス
class UserRepositoryImpl implements UserRepository {
    @Override
    public void save(User user) {
        // insert Database
    }
}
```

これで以下のような図の状態になり、「依存関係逆転の法則」をとりあえず満たした。

* 上位モジュールは下位モジュールに依存しない
* 上位も下位も抽象に依存すべき
* 抽象は実装の詳細に依存しない
* 実装の詳細が抽象に依存すべき

![図3.png](https://qiita-image-store.s3.amazonaws.com/0/203061/d8afb5e0-100c-caa2-2ce7-2f0dd9cc57b5.png)


ここで、この部分について、

```java
class User {
    private UserRepository repository; // フィールド変数にしてみた。
    private LocalDate birthDay;

    // コンストラクタで実装クラスのオブジェクトを受け取る。
    User(LocalDate birthDay, UserRepository repository) {
        this.birthDay = birthDay;
        this.repository = repository; // フィールド変数にセット。
    }

```

**誰かしらがフィールド変数`repository`をセットしないといけない**。その際、**依存関係逆転の法則に基づくと、Userクラスはデータアクセスを知ってはいけない**。依存性の注入とは、
    * 依存関係を,クラスやソースコードの外側から注入するものである
    * ソフトウェアのパターン
であるから、誰かがUserクラス以外の誰かがUserRepositoryImplの実装クラスオブジェクトをインジェクション(注入)しないといけない。


依存性の注入方法として、
たとえば、大前提として、アプリのメイン処理はモジュールの依存関係を知っているので、
以下のように依存性を注入できる。

```scala
public class Sample {
    public static void main(String[] args) {
        User user = new User(LocalDate.now(), new UserRepositoryImpl());
        user.save();
    }
}
```

この時の図はこんな感じ。

![図4.png](https://qiita-image-store.s3.amazonaws.com/0/203061/9e96ee56-3ca2-cda7-8083-d5a4644cb541.png)


* DIを使うメリットとして、DIを使うことで、`UserRepositoryImpl`だけでなく、`NoSQLUserRepositoryImpl`や`RDBUserRepositoryImpl`、`MQUserRepositoryImpl`などの実装クラス（`UserRepository`をインタフェースとする）を定義するなりしてそれらのオブジェクトをDIをロジック層を変更せずにできる。
* また、ここでは、メイン処理のSampleクラスで依存性を注入したが、**依存性の注入方法をconfigだったり、クラスだったりといった、注入する仕組みを提供するのがフレームワークで提供してくれるDIコンテナ**。

## 2. ScalaでのDI

大きく割ると、以下の方針があるらしい。（勉強中。。）

1. DIコンテナなどを使った**動的なDI**
  * メリット
      * Javaのわりと優秀なDIコンテナ(Guice とか) が使えて比較的取っ付きやすい
  * デメリット
      * 型安全ではない
2. `cake pattern`, `implicit parameter`, `macro`, `reader monad`などを利用した**静的なDI**
  * メリット
      * 型安全
  * デメリット
      * デメリットは trait だの macro だの implicit parameter だの、コンパイル時間が伸びる要因満載であること
      * コードが少し複雑になってしまう傾向

## 3. Play FrameworkでのDIのポイント

引用) http://labs.septeni.co.jp/entry/2017/12/11/212502
↑すごいためになった。まんま引用。

ポイントは以下。動的なDIを活用する。

* 機能の実装はコンストラクタにJSR 330アノテーションをつける`@Inject`で注入可能なこと示す
* Google Guiceでモジュールを書く。
* モジュール(bindingの提供)は細かく保つようにする。
* 単体アプリでは、`Guice.createInjector()`で注射器を作ってインジェクションする。


```scala
import javax.inject.Inject

import com.google.inject.{AbstractModule, Guice, Injector}

object Main {
  def main(args: Array[String]): Unit = {

    // モジュールを引数に注射器を作る。
    val injector: Injector = 
      Guice.createInjector(new TokyoMarathonModule, new MarathonRaceModule)
    val marathonRace: MarathonRace = 
      injector.getInstance(classOf[MarathonRace])
    marathonRace.venue()

    // 注射器は複数作れる。
    val injector2: Injector = 
      Guice.createInjector(new HonoluluMarathonModule, new MarathonRaceModule)
    val marathonRace2: MarathonRace = 
      injector2.getInstance(classOf[MarathonRace])
    marathonRace2.venue()

  }
}


trait Marathon {
  def value: String
}

class TokyoMarathon extends Marathon {
  override def value: String = "Tokyo"
}

class HonoluluMarathon extends Marathon {
  override def value: String = "Honolulu"
}

// Guiceモジュール
class TokyoMarathonModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[Marathon]).to(classOf[TokyoMarathon])
  }
}

class HonoluluMarathonModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[Marathon]).to(classOf[HonoluluMarathon])
  }
}


trait MarathonRace {
  def venue(): Unit
}

// MarathonRaceImplは、Marathonに依存することをJSR330のアノテーションで表現する。
// @Injectで注入可能なことを示す。
class MarathonRaceImpl @Inject()(marathon: Marathon) extends MarathonRace {
  override def venue(): Unit = println(marathon.value)
}

class MarathonRaceModule extends AbstractModule {
  override def configure(): Unit = {
    // MarathonRaceが必要な時はMarathonRaceImplを使う。
    bind(classOf[MarathonRace]).to(classOf[MarathonRaceImpl])
  }
}
```