LoginSignup
162
147

More than 5 years have passed since last update.

PHPでDI(Dependency Injection)

Last updated at Posted at 2013-11-24

何かと話題の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')の結果は同じになります。

162
147
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
162
147