DIの使用を開始する方法

DIは複雑でかさばる、遅い、「大規模」プロジェクトにのみ適しているため、現在のタスク(500以上のモデルクラス、300以上のコントローラクラス)での使用は不当であるという意見に繰り返し遭遇しました。 これは、DIがSymfonyの「依存性注入コンポーネント」のようなパッケージに明確に関連付けられているという事実に一部起因しています。

ここで、依存関係の反転自体が非常にシンプルで簡潔になり得ることを示すために、概念自体の理解を提供する特定の機能的な最小値を与えたいと思います。



内容



実装は、500行のコードの2つのクラスです。

SimpleDi \ ClassManager-クラスに関する情報を提供します。 本格的な作業には、キャッシュが必要です(Doctrine \ Common \ Cache \ ApcCacheを使用します)。これにより、スクリプトを呼び出すたびにリフレクションを作成できなくなります。 後続の注入のために注釈を解析します。 ブートローダーで次のように使用することもできます。 クラスファイルへのパスを格納します。

SimpleDi \ ServiceLocator-要求されたサービスを作成および初期化します。 注入を行うのはこのクラスです。

1)最も単純な場合、クラスに設定が設定されていない場合、SimpleDi \ ServiceLocatorはマルチトンパターン(別名オブジェクトプール)と同様に機能します。

$service_locator->get('HelperTime');
      
      





2)フィールドを介した実装オプション

 class A { /** * @Inject("HelperTime") * @var HelperTime */ protected $helper_time; } $service_locator->get('A');
      
      





このオプションは、次のようにコントローラーでのみ使用する必要があります。 実装のためにリフレクションが作成され、パフォーマンスに悪影響を及ぼします。 1つのクラスは、ページの読み込み中に複数のフィールドを持つスクリプトの呼び出しに影響しませんが、どこでも使用すると、パフォーマンスの低下が顕著になります。

ここで、Symfonyに向かって余談します。 そこでは、同様の実装が許可されます。



実装では、リフレクションは常に作成され、実装は常に正常に終了します。

3)メソッドによる実装

 class B { /** * @var HelperTime */ protected $helper_time; /** * @Inject("HelperTime") * @param HelperTime $helper */ public function setHelperTime($helper) { $this->helper_time = $helper; } } $service_locator->get('B');
      
      





このオプションは最も受け入れやすく、フィールドを介した実装とともに、デフォルトで依存関係を設定するために使用する必要があります。

4)構成による展開

 $service_locator->setConfigs(array( 'class_b_service' => array( 'class' => 'B', 'calls' => array( array('setHelperTime', array('@CustomHelperTime')), ) ) )); $service_locator->get('class_b_service');
      
      





これが依存性注入の使用目的です。 これで、設定を介してクラスBで使用されるヘルパーを置き換えることができますが、クラスB自体は変更されません。

5)クラスの新しいインスタンスを作成します。 同じクラスの複数のオブジェクトが必要な場合は、ServiceLocatorをファクトリとして使用できます

 $users_factory = $service_locator; $users_row = array( array('id' => 1, 'name' => 'admin'), array('id' => 2, 'name' => 'guest'), ); $users = array(); foreach ($users_rows as $row) { $user = $users_factory->createService('User'); $user->setData($row); }
      
      









任意の有用なライブラリを取得して、プロジェクトに実装してみてください。 これがgithub.com/yiisoft/yii/blob/master/framework/utils/CPasswordHelper.phpだとしましょう

クラスは絶対に不要なクラスYiiとCExceptionに厳密に結び付けられているため、これを実行できないことがわかります。

 class CPasswordHelper { … public static function generateSalt($cost=13) { if(!is_numeric($cost)) throw new CException(Yii::t('yii','{class}::$cost must be a number.',array('{class}'=>__CLASS__))); $cost=(int)$cost; if($cost<4 || $cost>31) throw new CException(Yii::t('yii','{class}::$cost must be between 4 and 31.',array('{class}'=>__CLASS__))); if(($random=Yii::app()->getSecurityManager()->generateRandomString(22,true))===false) if(($random=Yii::app()->getSecurityManager()->generateRandomString(22,false))===false) throw new CException(Yii::t('yii','Unable to generate random string.')); return sprintf('$2a$%02d$',$cost).strtr($random,array('_'=>'.','~'=>'/')); } }
      
      





クラスを任意のプロジェクトで使用できるようにするには、依存関係を正しく記述するだけで十分です。

 class CPasswordHelper { /** *      public ,        , *    . * @Inject * @var \Yii\SecurityManager */ public $securityManager; /** *   * @Inject * @var \YiiExceptor */ public $exceptor; … public function generateSalt($cost=13) { if(!is_numeric($cost)) $this->exceptor->create('yii','{class}::$cost must be a number.',array('{class}'=>__CLASS__)); $cost=(int)$cost; if($cost<4 || $cost>31) $this->exceptor->create('yii','{class}::$cost must be between 4 and 31.',array('{class}'=>__CLASS__)); if(($random=$this->securityManager->generateRandomString(22,true))===false) if(($random=$this->securityManager()->generateRandomString(22,false))===false) this->exceptor->create('yii','Unable to generate random string.'); return sprintf('$2a$%02d$',$cost).strtr($random,array('_'=>'.','~'=>'/')); } }
      
      





そして、クラスを取得する-例外ジェネレーター

 class YiiExceptor { public function create($a, $b, $c = null) { throw new CException(Yii:t($a, $b, $c)); } }
      
      







おわりに



DIを使用すると、モジュールがどのコンテキストで使用されるかを考える必要がなくなります。 (多くの場合、階層的な)依存関係のセットなしで、別のクラスを別のプロジェクトに転送することができます。 注釈を使用する場合、オブジェクトの明示的な作成、およびオブジェクトへのパラメーターとサービスの明示的な転送を処理する必要はありません。 そしてもちろん、そのようなクラスは、ファクトリを使用する代わりに、静的メソッドに関連付けたり、クラスのインスタンスを明示的に作成したりするよりも、テストしやすい場合があります。



参照資料



github.com/mthps/SimpleDiサンプル自体

理論en.wikipedia.org/wiki/Dependency_Injection

symfony.com/doc/current/components/dependency_injection/index.htmlの最高の実装の1つ



All Articles