DIとDIコンテナについて/Dependency Injection

## DI(Dependency Injection)とは

DI(Dependency Injection)を日本語に置き換えると「依存性(Dependency)の注入(Injection)」という意味です。

もっと分かりやすくできないかなって思ったところwikipediaには下記のように記載されており、

> A dependency is an object that can be used (a service).
ref: https://en.wikipedia.org/wiki/Dependency_injection

「Dependency」は「オブジェクト」という意味に置き換えられる。
つまり「オブジェクトの注入」という意味で解釈できます。

## DIコンテナとは

DIを利用するにあたって、毎回生成して注入してというのが大変なのでそれをコンテナ化して取り出すだけで利用できる状態にしたものをDIコンテナといいます。

## DIを使うことでどんなメリットがあるのか

最大のメリットは、「使う側と使われる側を切り離すことができる」ということ。
切り離すことによって、実体クラスに依存しない疎結合な状態になり変更やテストなどが行いやすくなります。

## DIの具体例

今回はコンストラクタでDIする場合(コンストラクタインジェクション)の具体例をPHPでご紹介します。

### DIじゃない場合

```php
<?php

class DisplayClass
{
    private $store;

    public function __construct()
    {
        $this->store = new CurryStore();
    }

    public function display()
    {
        echo $this->store->getName() . 'は' . $this->store->getPrice() . 'でした！';
    }
}

$displayClass = new DisplayClass();
$displayClass->display();
```

DisplayClassクラス内でCurryStoreクラスのインスタンスが生成されています。

これを行ってしまうとDisplayClassクラスはCurryStoreクラスに強く依存(CurryStoreクラスを持っている状態)してしまうため単体テストが行えなくなってしまいます。

これを解消するためにDIを利用します。

## DIを利用した場合

```php
<?php

class DisplayClass
{
    public function __construct(
        private readonly StoreInterface $store
    ) {
    }

    public function display()
    {
        echo $this->store->getName() . 'は' . $this->store->getPrice() . 'でした！';
    }
}

$displayClass = new DisplayClass(new curryStore());
$displayClass->display();
```

constructの引数にStoreInterfaceを持ってくることで外からCurryStoreクラスのオブジェクトを注入することでDisplayClassクラスはCurryStoreクラスを必要とせず自由度が高くなります。

例えば、こんなこともできます。(一例である「コンストラクタインジェクション」という方法)

```php
// 抽象クラスStoreInterface
interface StoreInterface
{
    public function getName(): string;
    public function getPrice(): int;
}

// StoreInterfaceの具象クラスCurryStore
class CurryStore implements StoreInterface
{
    public function getName(): string
    {
        return 'カレー';
    }

    public function getPrice(): int
    {
        return 1000;
    }
}

// StoreInterfaceの具象クラスHamburgerStore
class HamburgerStore implements StoreInterface
{
   public function getName(): string
    {
        return 'ハンバーガー';
    }

    public function getPrice(): int
    {
        return 500;
    }
}

class DisplayClass
{
    public function __construct(
        private readonly StoreInterface $store
    ) {
    }

    public function display()
    {
        echo $this->store->getName() . 'は' . $this->store->getPrice() . 'でした！';
    }
}

$displayClass = new DisplayClass(new HamburgerStore());
$displayClass->display(); // ハンバーガーは500円でした！
```

今回はHamburgerStoreクラスを引数としたのでgetName「ハンバーガー」、getPrive「500」の情報を扱ってます。この引数をCurryStoreクラスに変えると表示は、「カレーは1000円でした！」とすぐに切り替えることができます。

なのでまとめると、DisplayClassクラスの引数に具象クラスを渡してあげるだけで、具象クラスの情報を扱うことができるのがメリットです。

DIでない場合は1つの具象クラスに依存してしまうのでこういったことはできないので、DIを利用すると自由度が増したといえます。

## まとめ

今回DIについてコード例を含めご紹介しました。
例に利用したコンストラクタインジェクションの他にもメソッドインジェクションやンストラクタインジェクションなど色々な方法があります。
それぞれのメリット、デメリットを知っておくと使える幅が広がります。

## 参考

- Qiita：DIとDIコンテナを3分で理解する
https://qiita.com/hinom77/items/1d7a30ba5444454a21a8

- Qiita：初学者でも5分で理解できるようにDI(Dependency Injection)を説明してみた
https://qiita.com/hirodragon/items/73442390d551fe7376ea