Advent Day 4: Manual DI
DI考察ブログエントリー
@Hirakuさんが自身のブログで「PHPのDIで動的にオブジェクトを確保する考察」というDIのエントリーをポストされてます。
Dependency InjectionがPHPでも流行っているそうです。が、未だによくわからないので、わからないところを自分なりに考察してみます。
この記事の中で@Hirakuさんは特定のDIコンテナを使わないで、DIというパターンを考察しています。依存を手動で渡す様々な方法を試し、それらのメリット、デメリットを考察しています。
確かに、DBやLoggerなど、クラスの中で一つだけあれば十分なものも多く、そういうオブジェクトはコンストラクタに渡す形で「外から突っ込む」ことができるでしょう。
しかし、動的にオブジェクトを作るケースだって山ほどあるはずです。「必要なオブジェクトはクラスの生成時に全部できあがっている」なんて状態はそうそうあるもんじゃありません。
@hiraku DIの記事読ませて頂きました。後半のConfigの例についてですが、ランタイムにしか決まらない内容があるのであれば、それはオブジェクトのコンストラクションとは別メソッドに分けるという方向になるかと私は思います。
— Hidenori Gotoさん (@hidenorigoto) 12月 2, 2012
この記事の例で言うと基本的には@hidenorigotoさんの意見に+1です。
3つのInject
エントリーの中でランタイムでの動的取得に対して3つの方法をあげられています。
- factoryをinjectする
- クラス名をinjectする
- プロトタイプをinjectする
他にいいやり方があったら教えてください。
…考えてみました。
例えばfactory名をinjectするというのはどうでしょうか?
利用コードはクラス名を渡すのと同様です
型の保証が必要でないならsetterメソッドを除去して直接代入します。
また、以下のようなインスタンス生成スクリプトで渡す方法も考えてみました。1
これらの方法は懸念された問題のうちいくつかを解決しています。factoryによる自由な生成と呼び出しコードの簡素化、必要個数のランタイムでの取得も可能になります。
手動DIの限界
しかしやはり、考察を続けると色々な懸念が出て来ます。これらのインジェクトをシングルトン、あるいは限定個数で行う場合は?また依存にもまた依存が必要です。それらの依存もアプリケーションコンテキストで変化していきます。
以下はBEAR.Sundayのリソースクライアントの生成コードです。
こんな複雑なオブジェクトの生成をコンストラクタで毎回記述するわけにもいきません。factoryを利用することになりますが、ではこれらの生成のうち一部だけを違うものに差し替えたりするのはどうしたらいいでしょうか?実際にBEAR.SundayではDevモードの時はInvokerはDevInvokerに変わっています。ContainerもPersistentContainerに変更したいと思っています。
このインスタンスを都度生成するのではなくてシングルトンで供給したい時にはどうしたらいいでしょうか?あるいはこの生成にも依存が必要ならどうしたらいいでしょうか?その依存にもそのまた依存があるはずです。
手動によるDIを実践あるいは深く考察することはDIの理解に有意義な事ですが、ある時点からその限界が見えて来る事に気付きます。テストコードやごく小規模の開発では問題にならないでしょう。しかしこういう手続きの記述で複雑なオブジェクトを構成するのは、柔軟性や結合度、DRY原則で問題が出て来ます。しかしそれは同時にDependency Injectorにどのような役割や機能が求められるかの考察にもなるでしょう。
**
オブジェクトの生成・構成を手続きの記述ではなくルールに基づいたロジックで行う**ことでこれらの問題を解決します。次回Day 5はその考察を行います。
- factoryの方が良さそうですね… [↩]