自作Diコンテナに「名前空間」を実装してみた。 欲しいと思った理由をまとめてみました。
WScore/DiContainer : https://github.com/asaokamei/WScore.DiContainer
アノテーションなどを使って、自動で依存性を解決してくれる DIコンテナはとても便利です。
例えば、こんな依存性があった場合。
class Model
{
/**
* @Inject
* @var Query
*/
public $query;
}
$container->set( 'pdo', 'mysql:db=test' );
$model = $container->get( 'Model' );
// produces
// $model->query->sql->dba->pdo(mysql);
Queryがとても深い依存性を持っているにもかかわらず、 自動で依存性を解決してくれるので、コードがシンプルになり、 開発が簡単になります。
###複数の設定を使い分けたい
複数のモデルで、異なるpdoを使いたい場合、 自動での依存性の解決ができなくなります。
例えば$model1はMySQL、$model2がSqliteを使う場合です。
$model1->query->sql->dba->pdo(MySql)
$model2->query->sql->dba->pdo(Sqlite)
異なるのは最後のPDOだけにもかかわらず、 途中のクラス全ての設定をする必要がでてきます。
例えば、次のようにQuery以下全てを指定することになります。
$container->set( 'Model2' )->property( 'query', 'query-sqlite' );
$container->set( 'query-sqlite', 'Query' )->property( 'sql', 'sql-sqlite' );
$container->set( 'sql-sqlite', 'sql' )->property( 'dba', 'dba-sqlite' );
$container->set( 'dba-sqlite', 'dba' )->property( 'pdo', 'pdo-sqlite' );
$container->set( 'pdo-sqlite', 'sqlite:/folder/sqlite/test' );
これを簡単にするため名前空間というのを導入してみました。
名前空間の基本は、オブジェクトのIDと一緒に名前空間 (namespace)を設定します。 IDに名前空間をつけることで、ひとつのIDに対して複数の設定を 指定できるようになります。
ただし、これだけでは簡単になりません。 そこで、名前空間+IDで設定がなければIDでの設定を利用することにしました。
既存の設定を利用することで、必要な箇所だけ設定を上書きできるはずです。
###名前空間を使った依存性の解決
例として、Model2に別の名前空間をしていしてみます。
/*
* namespace myModel2
*/
class Model2
{
/**
* @Inject
* @var Query
*/
public $query;
}
$container->set( 'pdo', 'mysql:db=test' );
$container->namespace( 'myModel2' );
$container->set( 'pdo', 'sqlite:db=test' );
$container->namespace();
$model1 = $container->get( 'Model' );
$model2 = $container->get( 'Model2' );
Model2はmyModel2という名前空間に存在します。
Model2を生成するときは、myModel2の名前空間にあるIDを探しますが、 ほとんど設定がないので、基本的に既存の依存性を利用します。
ただし最後のPdoだけはSqliteが設定されているので、これを利用します。
これで「とても簡単に」複数の設定を使い分けることができました。
一方、設定がひとつの場合は、名前空間を無視して使えば 以前と同じように使うことができます。
これはModel2が特定の依存のセットを"myModel2"を前提としている、つまり依存をクラス単位で考え、ソースのものとその差分の依存を解決するというものでしょうか。
対して、Guiceでいう"Binding Annotations" あるいは@nAmed(Ray.Diはこれのみ)はアノテーションのバインディングで個別のバインディングに依存側に名前の注釈をつけて解決します。