DIは美しいデザインパターン

こんにちは。お久しぶりです。

Springで開発中なのですが、なんとも難しい。。。
今日はDIについて書こうかなと思います。

# DI(依存性注入)の定義

> コンピュータプログラムのデザインパターンの一つで、オブジェクトなどの間に生じる依存関係をオブジェクトのコードに直接記述せず、外部からなんらかの形で与えるようにする手法。

これを理解するためには、「依存」という意味と「注入」という意味を理解する必要がある。

# 依存

依存とは一言で言うと「他のクラスを利用しているかどうか」のことである。

例えば下記のコードがあるとする。

```Car.java
public class Car {

    private HondaEngine hondaEngine;

    public Car(HondaEngine hondaEngine) {
        this.hondaEngine = hondaEngine;
    }

    public void go() {
        hondaEngine.startEngine();
        hondaEngine.stopEngine();
    }
}
```

```HondaEngine.java
public class HondaEngine {

    public void startEngine() {
        System.out.println("ホンダスタート");
    }

    public void stopEngine() {
        System.out.println("ホンダストップ");
    }
}
```

上記のクラスをER図で書くと下記のようになる。
![スクリーンショット 2021-10-26 19.06.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/84a97d26-e121-3559-e036-ad164181553f.png)


このコードであれば下記の問題が発生する。
1. HondaEngineaが完成するまで、Carクラスを動かすことができない。
2. HondaEngineからNissanEngineに変更したいときに修正する箇所が多い。

上記のような依存関係が強いものを「**密結合**」と呼ぶ。

この依存を弱くするためにインターフェースを使う。

下記が上記のコードを書き換えた例である。

```Engine.java
public interface Engine {
    public void startEngine();

    public void stopEngine();
}
```

```HondaEngine.java
public class HondaEngine implements Engine {

    public void startEngine() {
        System.out.println("ホンダスタート");
    }

    public void stopEngine() {
        System.out.println("ホンダストップ");
    }
}
```

```Car.java
public class Car {

    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void go() {
        engine.startEngine();
        engine.stopEngine();
    }
}
```

上記であれば、先ほどと比べて変更に強い構造になっている。

変更に強いとは下記の２点のことである。
1. HondaEngineができていなくても、テスト用Engineを作れば、Carクラスは実行できる。
2. HondaEngineからNissanEngineに変更したとしても、ここの2つのクラスでは変える必要ない。

このように、各クラスが依存度が低いときを「**疎結合**」と呼ぶ。

以上が依存についてである。

# 注入

注入とは
**そのクラスの外から定数、変数、インスタンスをあるクラスに代入すること**である。

先程のコードの続きを考えてみます。


```Engine.java
public interface Engine {
    public void startEngine();

    public void stopEngine();
}
```

```HondaEngine.java
public class HondaEngine implements Engine {

    public void startEngine() {
        System.out.println("ホンダスタート");
    }

    public void stopEngine() {
        System.out.println("ホンダストップ");
    }
}
```

```Car.java
public class Car {

    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void go() {
        engine.startEngine();
        engine.stopEngine();
    }
}
```

これに追加して、プログラムを実行するMainクラスを考えます。

```Main.java
public class Main {

    public static void main(String[] args) {
        Engine hondaEngine1 = new HondaEngine();
        Engine hondaEngine2 = new HondaEngine();
        Engine hondaEngine3 = new HondaEngine();

        Car car1 = new Car(hondaEngine1);
        Car car2 = new Car(hondaEngine2);
        Car car3 = new Car(hondaEngine3);

        car1.go();
        car2.go();
        car3.go();
    }
}
```

上記のようにインターフェースに変数を代入することを**注入**という。

少し話は脱線するが、上記は柔軟性に欠けるので、Factoryクラスで解決すると良い。
説明は避けるが下記に変更するとより良い。

```Factory.java
public class Factory {
    
    public static Engine createHondaEngine() {
        return new HondaEngine();
    }
}
```

```Main.java
public class Main {

    public static void main(String[] args) {
        Engine hondaEngine1 = Factory.createHondaEngine();
        Engine hondaEngine2 = Factory.createHondaEngine();
        Engine hondaEngine3 = Factory.createHondaEngine();

        Car car1 = new Car(hondaEngine1);
        Car car2 = new Car(hondaEngine2);
        Car car3 = new Car(hondaEngine3);

        car1.go();
        car2.go();
        car3.go();
    }
}
```

上記にコードを修正することによって、hondaEngineを継承しUpdateしたクラスにもFactoryクラスのnew クラスを変更するだけで対応が可能である。

ここまでが、依存と注入の説明である。

### DIに話を戻す。
DIとは上記の2つを同時に、簡単に行ってくれる仕組みである。SpringではDIを自動化してくれるDIコンテナを提供している。

DIによって得られるメリットは下記である。

```
・コードが簡素になり、開発期間が短くなる
・テストが容易になり、「テスト・ファースト」による開発スタイルを取りやすくなる
・特定のフレームワークへの依存性が極小になるため、変化に強いソフトウエアを作りやすくなる（＝フレームワークの進化や、他のフレームワークへの移行に対応しやすくなる）
```

ここからはSpring上でDIがどう処理されているか、どう実装するかを考察していく。

# DIの処理の流れ
全体の流れは以下の２つである。
1. コンポーネントスキャン
2. インスタンスの作成と注入

### コンポーネントスキャン
インスタンスの作成と注入をするための対象クラスを探し出す。
下記はイメージ図
![スクリーンショット 2021-10-26 19.52.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/681c230b-0b25-2a45-d7f2-5803dd57d6b6.png)

上記のように特定のアノテーションがついたクラスをDIコンテナに追加していく。
特定のアノテーションとは
- @Service
- @Controller
- @Entity
- @Repository
- @Configuration
- etc...

上記のようなアノテーションがついている、DIコンテナに登録されるJavaクラスを**Bean**と呼ぶ。


### インスタンスの作成と注入
コンポーネントスキャンが終了した後は、DIコンテナが自動で対象のBeanをインスタンス化する。**このときに、呼ばれるときに毎回インスタンス化するのか、シングルトンなのかも含めて設定される。**ここがDIのもう一つの強みである。一行でこの設定ができるのは本当にありがたい。ここまでで依存が解決されたインスタンス(生成するためのロジック)が完了している。

生成されたインスタンスは@Autowiredアノテーションがついたフィールドに注入(代入)される。ちなみに@Autowiredアノテーションがつけられるのは、
- フィールド変数
- コンストラクタの引数
- setterの引数
のみである。

DIの実装はまた今度。

DIについては少しわかったけど、次の難点はコードに落とし込むところだなぁ。。。。