id:anatooのブログ RSSフィード

 | 

2008年12月15日

最小のDIコンテナ in PHP

DIコンテナがなにやら大仰なものとして勘違いされているような気がしたので、機能を極限まで削ぎ落とした最小のDIコンテナを書いた。

これにはAOPは当然ないし、設定ファイルなどもない。

<?php
// DIContainer.php

class DIContainer
{
    protected $componentFactory;
    function __construct(ComponentFactory $c)
    {
        $this->componentFactory = $c;
        $c->accept($this);
    }

    function get($name)
    {
        $name = strtolower($name);
        if (!isset($this->objects[$name])) {
            $this->objects[$name] = $this->componentFactory->get($name);
        }

        return $this->objects[$name];
    }
}

abstract class ComponentFactory
{
    protected $container;
    function get($name)
    {
        return $this->{'build' . $name}();
    }

    function accept(DIContainer $c)
    {
        $this->container = $c;
    }
}

非常に短いですね。

このコンテナをどう使うか

PDOのインスタンスを管理するシンプルな例を用意した。

<?php
include_once dirname(__FILE__) . '/DIContainer.php';

class MyComponentFactory extends ComponentFactory
{
    function buildConfig()
    {
        $config = new stdClass();
        $config->db = 'mysql';
        $config->dbname = 'hoge';
        $config->host = 'localhost';
        $config->user = 'dbusername';
        $config->password = 'dbpassword';
        return $config;
    }

    function buildPDO()
    {
        $config = $this->container->get('config');
        $dsn = "{$config->db}:dbname={$config->dbname};host={$config->host}";
        $pdo = new PDO($dsn, $config->user, $config->password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $pdo;
    }
}

$container = new DIContainer(new MyComponentFactory);

// オブジェクトをコンテナから取り出す
$pdo = $container->get('pdo'); 

DIコンテナの利点の一つとして、ロジックを含まないインスタンスの生成を引き受けてくれるという点が挙げられる。

このコンテナだとDIContainer::getメソッドで得られるのはシングルトンなオブジェクトのみだが、もっと機能を持つDIコンテナならgetするたびに新しいインスタンスを得たりというようなこともできるだろう。


DIという単語は日本語に直しても依存性の注入という抽象的な言葉になるので取っ掛かりがなく、わかり辛い。

また、ウェブに転がってるDIに関する文章も一般のデザインパターンに関する文章などと比べて抽象的で難しいものが多い。

が、DIコンテナ自体はそんなに複雑でわかり辛いことをやっているわけではない。

最初の取っ掛かりとしては、DIコンテナはインスタンスの管理をやってくれる便利な奴、ぐらいの認識でいいと思う。


DIコンテナでクラス同士を疎結合にする例は、また後のエントリで示す。

anatooanatoo 2008/12/15 19:01 コード修正。
Componentと言いながら一つしかない!ということで、
DIComponent→ComponentFactoryへとクラス名を変更し、それに伴う部分も修正した。

tk1102tk1102 2008/12/16 11:30 これDIじゃなくて、所謂ServiceLocatorですよね?

anatooanatoo 2008/12/16 13:10 いえ、ServiceLocatorではありません。
管理されるオブジェクトはDIContainerクラスに依存していませんよ。

tk1102tk1102 2008/12/17 11:30 こんにちは。まあ、ファクトリーでもオブジェクトプールでも呼び方は何でもよかったんですけれども、コンテナが依存性を注入していない時点で、「DIコンテナ」とはよべなくないですか?

anatooanatoo 2008/12/17 12:20 コンテナが依存性を注入してますよ。
正確に言うとコンテナに組み込まれたComponentFactoryがですけど。
ComponentFactoryもDIコンテナ側として見て下さい。
それに実際にこれ使ってクラス同士を疎結合にする例を後でエントリに書きますんでそれ見てもらっていいですか?

tk1102tk1102 2008/12/19 10:59 ふたたびこんにちは。続くエントリーたのしみにしております。
まず、そもそもDIにはオブジェクトプールは必須要件ではないので、上記例からさらにそぎ落として見てください。すると残るのはDIではなく、AbstractFactoryになると思うのですがどうでしょうか。

anatooanatoo 2008/12/19 21:03 >まず、そもそもDIにはオブジェクトプールは必須要件ではないので、
DIじゃなくてDIコンテナですね。
DIコンテナにオブジェクトプールは必須要件ではない、というのは一理あると思います。
>すると残るのはDIではなく、AbstractFactoryになると思うのですがどうでしょうか。
オブジェクトの設定と利用の分離を実現するDIコンテナはファクトリーに似た部分を持っています。
ただそれでも上記のDIコンテナからオブジェクトプール的な機能を削ってもAbstractFactoryそのものにはなりませんよ。
というのも、AbstractFactoryはロジックを含むインスタンスの生成を可能であることを期待されますが、ComponentFactoryクラスではそうではないからです。
>DIコンテナの利点の一つとして、ロジックを含まないインスタンスの生成を引き受けてくれるという点が挙げられる。
と、記事で書いてある通りです。

tk1102tk1102 2008/12/19 23:50 ちょっとわからないのですが、「ロジックを含まないインスタンス」のロジックって何のことですか?

anatooanatoo 2008/12/20 00:33 ああ読みづらくて申し訳ない。
>「ロジックを含まないインスタンス」
じゃなくて
>「ロジックを含まないインスタンスの生成」
と読んでください。
ComponentFactoryのbuildなんとかメソッドでは、DIContainerのインスタンスから取り出したオブジェクト(これから生成するオブジェクトが依存しているオブジェクトです)と、静的なパラメータによってオブジェクトが生成されます。
有体に言うと、インスタンスを生成するときに引数をとって動的なことをしたり条件分岐したりしない、ということです。

anatooanatoo 2008/12/20 00:35 http://onestepback.org/articles/depinj/matz/matzdi_block_rb.html
似たようなDIコンテナを見つけました。
Rubyで書かれていますがやっていることはほぼ一緒ですのでイメージしやすいと思います。

anatooanatoo 2008/12/22 23:06 コードちょい修正。

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

 |