Help us understand the problem. What is going on with this article?

PHPでDI(Dependency Injection)

More than 5 years have passed since last update.

何かと話題のPHPでのDIについてまとめてみました。

そもそも DI(Dependency Injection)ってなんぞ?

その名の通り、 依存性(Dependency)注入(Injection)です。

依存をクラス内で生成せずに外から設定します。
まだパッとしないので具体例を挙げて説明してみます。

まずDIでないパターン

Car.php
class Car
{
    /**
     * @var EngineInterface
     */
    private $engine;

    public function __construct()
    {
        $this->engine = new Engine();
    }

    public function run()
    {
        $energy = $this->engine->burn();
    }
}

このコードの良くない点

  1. 別のエンジンに変えたい時にCar.phpを修正しなければならない。
  2. エンジンがうまく動作しない時のテストはほぼ不可能
  3. Engineが完成するまでCarを作る事ができない。

DIパターンを適用してみる

方法1: コンストラクターインジェクション

Car.php
class Car
{
    /**
     * @var EngineInterface
     */
    private $engine;

    /**
     * @param EngineInterface $engine
     */
    public function __construct(EngineInterface $engine)
    {
        $this->engine = $engine;
    }

    public function run()
    {
        $energy = $this->engine->burn();
    }
}

方法2: セッターインジェクション

Car.php
class Car
{
    /**
     * @var EngineInterface
     */
    private $engine;

    /**
     * @param EngineInterface $engine
     */
    public function setEngine(EngineInterface $engine)
    {
        $this->engine = $engine;
    }

    public function run()
    {
        $energy = $this->engine->burn();
    }
}

これで先述の問題点は解決できました。

1.別のエンジンに変えたい時にCar.phpを修正しなければならない。

Carを使う側が好きにすれば良い。

2.エンジンがうまく動作しない時のテストはほぼ不可能

テスト時に設定するエンジンを変えてしまえば良い。

3.Engineが完成するまでCarを作る事ができない。

開発中は仮のエンジン(モック)を作って使えば良い。

DIとはこういう事

意外と簡単ですねー

だが依存を手動で解決するには少し大変という話

CarFactory.php
class CarFactory
{
    public function get($type)
    {
        if ($type === 'economy') {
            return new Car(new HybridEngine(
                                new GasolineEngine(),
                                new ElectricMotor(new Battery())));
        }
        if ($type === 'monster') {
            return new Car(new V8Engine(6600));
        }
    }
}

引数が多くて管理しきれなくなる事は見えてます。
そこで登場するのが DIコンテナ

DI コンテナ

依存関係を定義しておくと、オブジェクトの生成時に依存を解決してくれるという代物です。
どう定義するかは、実際にコードを見た方が分かりやすいのでココでは省略。

ちなみにPHP製DIコンテナは意外と沢山あります。
有名どこはこんな所でしょうかね

Symfony/DependencyInjectionでサンプル

バンドルを作った際に{バンドルディレクトリ}/DependencyInjection/{バンドル名}BundleExtention.phpというファイルが生成されていると思います。

HogeBundleExtention.php
public function load(array $configs, ContainerBuilder $container)
{
    $configuration = new Configuration();
    $config = $this->processConfiguration($configuration, $configs);

    // デフォルトはXMLなのでYAMLフォーマットに変更します。
    $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
    $loader->load('services.yml');
}

Resources/config/services.ymlをDIの定義ファイルとして指定したので、先述のCarFactory.phpを定義として記述してみます。

Resources/config/services.yml
services:
  car.hybrid:
    class: Hoge\Car
    arguments: [@engine.hybrid]

  engine.hybrid:
    class: Hoge\Engine\Hybrid
    arguments: [@engine.gasoline, @engine.motor]

  engine.gasoline:
    class: Hoge\Engine\Gasoline

  engine.motor:
    class: Hoge\Engine\Motor
    arguments: [@battery]

  battery:
    class: Hoge\Battery

  1. それぞれを サービス として定義
  2. 依存しているサービスをメソッドの引数として定義

これで$container->get('car.hybrid')$factory->get('economy')の結果は同じになります。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away