Pimple風の名前ベースでインジェクション設定を書いていくやり方なら、ただのファクトリで再現できそうに思った。
Pimpleのコア機能はPimpleがなくても実現できる仮説
DIのサンプルはPHPメンターズのものを流用させていただく。
PHPメンターズ -> Pimpleでシンプルに正しくDIを理解する
Pimpleの場合。
<?php
$container = new \Pimple();
// オブジェクトコンストラクションのコンフィギュレーション
$container['infrastructure.mailer'] = function($container) {
$mailer = new \SendmailMailer();
return $mailer;
};
$container['domain.transfer.newsletter'] = function($container) {
$newsletterTransfer = new \NewsletterTransfer($container['infrastructure.mailer']);
return $newsletterTransfer;
};
return $container;
素のクラスにしてみる。
<?php
class FactoryDefault
{
function createInfrastructureMailer()
{
return new SendmailMailer;
}
function createDomainTransferNewsletter()
{
return new NewsletterTransfer($this->createInfrastructureMailer());
}
}
<?php
$container = new FactoryDefault;
// オブジェクトの利用
$newsletterTransfer = $container->createDomainTransferNewsletter();
$newsletterTransfer->send('ニュースレター本文');
メソッド名をラベルとして使うスタイルです。実現できてますね!
ファクトリによるDI設定のテクニック
オブジェクトの生成を使いまわす(疑似シングルトン)
static変数でキャッシュすればおk。
たとえばさっきの例でInfrastructureMailerは毎回作らなくても、同じオブジェクトを使いまわせばいいのなら、こんな感じか?
function createInfrastructureMailer()
{
static $mailer;
return $mailer ?: ($mailer = new SendmailMailer);
}
設定を上書きする
DIコンテナなんていらない - usagidropの日記
このページだと、ファクトリをグローバル関数で書いてしまっている。PHPの関数は一度定義すると上書きできないので、拡張性を担保するならばクラスで書いてメソッドにした方がよいだろう。
ファクトリによるDIコンテナなら上書きが可能である。
たとえばさっきの例で、テストをしたいからInfrastructureMailerはモックに差し替えたい、という場合。
<?php
class FactoryTest extends FactoryDefault
{
function createInfrastructureMailer()
{
return new SendmailMailerMock;
}
}
<?php
$container = new FactoryTest; //こっちに差し替える
//...
Mockに差し替えられた。
設定を継承して追加する
もちろんクラスの継承なので、parent::も使える。
たとえばさっきの例で、InfrastructureMailerに何か設定を追加したい場合。こんな感じでいける。
//...
function createInfrastructureMailer()
{
$mailer = parent::createInfrastructureMailer();
$mailer->setOption('hogehoge');
return $mailer;
}
//...
素のFactoryをDIコンテナとして利用するメリット
- 特定のDIコンテナライブラリが不要
- たぶん速い
- コア機能は割となんとかなりそうな気がする
- IDEで入力補完がきく
素のFactoryをDIコンテナとして利用するデメリット
- PHP<5.4だと設定ファイルの分割ができない。traitが必要
- 複数の設定ファイルをマージできない。traitが必要
- 所詮は名前ベースのインジェクタなので、大規模になると名前管理で破綻する
- AOPなどの豪華機能はない