Closed koriym closed 11 years ago
SmartyProviderもそれを最終的に利用するコンシュマーも拡張がある事に無関心です。
しかし、こうやってデフォルトの設定とユーザーの設定の合成をつくるのもいいですが、SmartyProviderをコピーしてMySmartyProviderをつくった方が単純でいいのではないか?とも思います。(DRYを比較的軽視してます)
なるほど、やはりAOP的にはそういう考え方になるのですね。
これは僕の勉強不足かもしれないのですが、結局このSmartyModuleには、ユーザのMySmartyConfigProviderクラスの知識が必要になって、MySmartyModule になってしまわないのでしょうか?
class MySmartyModule {
protected function configure()
{
// ...
$this
->bind('Smarty')
->annotatedWith('configured')
->toProvider(__NAMESPACE__ . '\MySmartyConfigProvider'); // ここ
こうなると、現実的には、BEAR\Package\Provideの下からMyApp\Moduleに、ModuleとConfigProviderをひな形としてコピーしてくることになり、あまり知識を隠蔽できていない(Providerによるちょっとした初期設定だけ隠蔽している)のに、コンテナ側の複雑さが増えてしまいますよね... ちがいます?
※ もし、自分のAppModuleの中にMySmartyConfigProviderと書けば、デフォルトのConfigProviderをすり替えることができるというなら、アプリケーションでは、自分で書くクラスが1つだけになり、SmartyModuleのDIがどうなっているかをほとんど知らなくていい、となるので、素敵だと思います。
いっぽうで、もしBEAR.Packageが、ユーザにとって「デフォルトの構成サンプル」という立ち位置なんだとすると、よりリッチに拡張されたSmartyを提供するProviderだったり、もっと器用に振る舞うTemplateEngineAdapterだったりは、「デフォルトを参考にして各自それぞれ実装してください」とするのもいいかもしれませんね。 BEAR.Sundayのコアはあくまで疎結合構成のフレームワークなので(DIとAOPのコンテナであり、具体的な機能はリソース提供)、Webアプリケーションフレームワークを作るのはアーキテクトの仕事です、そこにフレームワークとしての縛りはありません、という意味で。
DIの束縛は上書き/あるいは先取りすることができます。 AppModuleで
$this->bind('Smarty')->annotatedWith('configured')->toProvider('MyApp\MySmartyConfigProvider');
あるいは@Named("configured")使わないで
$this->bind('Smarty')->toProvider('MyApp\MySmartyProvider');
これで優先的に束縛されます。
あるいはこの1つだけの束縛をMyphptalModuleモジュールにしてPhptalModule渡すようなつくりでもいいと思います。
$this->install(new PhptalModule(new MyphptalModule));
しかしこのような設定はアプリケーションを横断してチームや個人で共有されるものです。その意味では言われるようなMyModuleをつくってしまって
$this->install(new MyPhptalModule);
でもいいと思います。
更にこれを進めると
$this->install(new MyPhptalModule);
$this->install(new MyOrmModule);
$this->install(new MyCacheModule);
とFWの各コンポーネントの束縛の集合となり
$this->install(new MyAppPackageModule);
アプリケーションのconfigurationとなります。 実装を持たないBEAR\Sunday\Extensionに対する、実装と束縛の集合のBEAR\Package\Provideになります
余談ですが、これらの設定はAOPとは関係ありません。
結論として
A) ユーザーのよる可変点を明確にしてその変更の規模が大きい時(ロジックがはいるようなもの)はこの例のように別モジュール B) 少数の定数のインジェクトなら@Namedで受ける(現在TmpDirを受けてるように) C) それ以外ならProviderをAppModuleで変更 (SmartyProvider -> MySmartyProvider)
でどうでしょうか。
DIの上書き、理解しました! 上書きできる発想がなかったです。ConfigProvider のアイデアで、ユーザは自分の欲しい拡張をそれ以外の事情を知らずに足せます。これいい。
結論は、自分でコントロールしたければ独自Module/独自Providerを作ることもできる、としつつ、標準では拡張ポイントとして ConfigProvider を挟んである、ということですね。自身がないので、Smartyがその形になったとき、PHPTALも追従します。
SmartyもPHPTalもこのサイズだったらConfigureModuleなしで元のProviderを入れ替えるだけで良いと思いますがどうでしょうか。
たしかに (^^
DIを使った、違う設定の仕方も紹介しましょう。
この例では 生成 => 設定 => 「利用への注入」とごく一般的なフローで捉えていますが、これに縛られる必要はありません。利用への注入を済ませてから設定をすることもできます。
まずこのようにデフォルトの設定で注入をした後に、
$this->install(new PhptalModule);
そのインスタンスをAppModuleで取り出し、設定を行う事ができるはずです。
class ProdModule
{
public function configure()
{
$phptal = $this->injector->getInstance('PHPTal');
$phtal->setConfigForProdA();
$phtal->setConfigForProdB();
}
}
先に注入を済ませたオブジェクトを後から変更できるのは直感に反する感じがあるのですが、これはインジェクターがPHPTalをシングルトンでデリバリーするので、セッターで注入されるオブジェクトも、後からインジェクターで取得するオブジェクトも同じものを指してるためです。
コンテキストに応じて設定を変更するのもこのようにコンテキスト別モジュールで自然にできます。
class DevModule
{
public function configure()
{
$phptal = $this->injector->getInstance('PHPTal');
$phtal->setConfigForDevA();
$phtal->setConfigForDevB();
}
}
最初のサンプルはインジェクションを行ってないのでをのままでは動きません。requestInjectionする必要があります。訂正します。
色々検討してみたのですがtoProviderでファクトリーを指定するようにtoModifierとか新設するのが良いのではないかと考えました。既存の設定に変更がなく、拡張が後から外付けできます。
SmartyProvider create Smarty instance.
SmartyConfigureProvider modify smarty object as we need.. This provider class can be changed by override binding.
SmartyModule connects all together.
SmartyAdapter needs @Named annotation to have "configured" dependency.
素案でこういうのを考えてみました。 ping @tanakahisateru