DI の背後にある要点は、クラスが依存するオブジェクトを作成して準備し、それらをプッシュすることからクラスを解放することです。これは非常に合理的に聞こえますが、クラスは、その機能を実行するためにクラスにプッシュされているすべてのオブジェクトを必要としない場合があります。この背後にある理由は、無効なユーザー入力、必要なオブジェクトの 1 つによって以前にスローされた例外、またはコード ブロックが実行されるまでオブジェクトをインスタンス化するために必要な特定の値が利用できない場合に発生する「アーリー リターン」です。
より実用的な例:
- ユーザーデータが検証に合格しないため、使用されることのないデータベース接続オブジェクトを挿入します (このデータを検証するためにトリガーが使用されていない場合)。
- 入力を収集する Excel のようなオブジェクト (PHPExcel など) を挿入する (ライブラリ全体が取り込まれて使用されないため、読み込みとインスタンス化が重くなります。書き込みが発生する前に検証が例外をスローするためです)。
- 実行時にインジェクターではなく、クラス内で決定される変数値。たとえば、ユーザー入力に基づいて呼び出されるコントローラー (またはコマンド) クラスとメソッドを決定するルーティング コンポーネント
- これは設計上の問題かもしれませんが、かなりのサービスクラスであり、多くのコンポーネントに依存しますが、リクエストごとにそれらの 1/3 程度しか使用しません (理由は、コントローラーの代わりにコマンドクラスを使用する傾向があるためです)。
したがって、必要なすべてのコンポーネントをプッシュすることは、一部のコンポーネントが作成されて使用されないという点で「遅延読み込み」と矛盾します。これは少し非現実的で、パフォーマンスに影響を与えます。PHP に関する限り、より多くのファイルがロード、解析、およびコンパイルされます。プッシュされるオブジェクトに独自の依存関係がある場合、これは特に苦痛です。
私はそれを回避する方法を 3 つ見ていますが、そのうちの 2 つはうまく聞こえません。
- 工場への注入
- インジェクターの注入 (アンチパターン)
- 関連するポイントに到達すると、クラス内から呼び出される外部関数を注入します (「データ検証が終了したら、PHPExcel インスタンスを取得する」などの smtg)。これは、その柔軟性のために私が使用する傾向があるものです
問題は、そのような状況に対処する最善の方法は何ですか / 皆さんは何を使っていますか?
更新: @GordonM ここに 3 つのアプローチの例があります。
//inject factory example
interface IFactory{
function factory();
}
class Bartender{
protected $_factory;
public function __construct(IFactory $f){
$this->_factory = $f;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = $this->_factory->factory(); //! factory instance * num necessary components
$db->insert('orders', $data);
//...
}
}
/*
inject provider example
assuming that the provider prepares necessary objects
(i.e. injects their dependencies as well)
*/
interface IProvider{
function get($uid);
}
class Router{
protected $_provider;
public function __construct(IProvider $p){
$this->_provider = $p;
}
public function route($str){
//... match $str against routes to resolve class and method
$inst = $this->_provider->get($class);
//...
}
}
//inject callback (old fashion way)
class MyProvider{
protected $_db;
public function getDb(){
$this->_db = $this->_db ? $this->_db : new mysqli();
return $this->_db;
}
}
class Bartender{
protected $_db;
public function __construct(array $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
$provider = new MyProvider();
$db = array($provider, 'getDb');
new Bartender($db);
//inject callback (the PHP 5.3 way)
class Bartender{
protected $_db;
public function __construct(Closure $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
static $conn = null;
$db = function() use ($conn){
$conn = $conn ? $conn : new mysqli();
return $conn;
};
new Bartender($db);