DIに関するよくある質問(FAQ)
DIはIoCの別名ですか?
*Inversion of Control*(IoC)は、コードがどのように実行されるかに焦点を当てた原則です –
あなたのコードが外部のコードを実行するのか、それともあなたのコードが外部のコードに統合され、その後呼び出されるのか。
IoCは、イベント、いわゆるハリウッド原則、およびその他の側面を含む広範な概念です。
この概念の一部には、ルールNo.3:ファクトリに任せるで説明されているファクトリも含まれ、これらはnew
演算子の逆転を表します。
*Dependency Injection*(DI)は、あるオブジェクトが別のオブジェクト、つまりその依存関係についてどのように知るかに焦点を当てています。これは、オブジェクト間で依存関係を明示的に渡すことを要求する設計パターンです。
したがって、DIはIoCの特定の形式であると言えます。ただし、すべての形式のIoCがコードの純粋性の観点から適切であるわけではありません。たとえば、アンチパターンの中には、グローバル状態を操作する手法や、いわゆるサービスロケータがあります。
サービスロケータとは何ですか?
これはDependency Injectionの代替案です。利用可能なすべてのサービスまたは依存関係が登録される中央リポジトリを作成することで機能します。オブジェクトが依存関係を必要とするとき、Service Locatorにそれを要求します。
ただし、Dependency Injectionと比較して、透明性が失われます:依存関係はオブジェクトに直接渡されず、簡単には識別できないため、すべての関連性を明らかにし理解するにはコードを調査する必要があります。テストもより複雑になります。なぜなら、テスト対象のオブジェクトにモックオブジェクトを単純に渡すのではなく、Service Locatorを介して行う必要があるからです。さらに、Service Locatorはコードの設計を損ないます。個々のオブジェクトはその存在を知る必要があるため、オブジェクトがDIコンテナを認識しないDependency Injectionとは異なります。
DIを使用しない方が良い場合はいつですか?
設計パターンDependency Injectionの使用に関連する既知の困難はありません。逆に、グローバルに利用可能な場所から依存関係を取得することは、多くの合併症につながり、Service Locatorの使用も同様です。 したがって、常にDIを使用することが推奨されます。これは独断的なアプローチではなく、単により良い代替案が見つからなかったためです。
それでも、オブジェクトを渡さずにグローバル空間から取得する特定の状況があります。たとえば、コードのデバッグ中に、プログラムの特定のポイントで変数の値を出力したり、プログラムの特定の部分の期間を測定したり、メッセージを記録したりする必要がある場合です。 このような場合、後でコードから削除される一時的なタスクである場合、グローバルに利用可能なダンパー、ストップウォッチ、またはロガーを利用することは正当です。これらのツールはコードの設計には属しません。
DIの使用には欠点がありますか?
Dependency Injectionの使用には、たとえばコード記述の複雑さの増加やパフォーマンスの低下などの欠点がありますか? DIに従ってコードを書き始めたときに何を失いますか?
DIはアプリケーションのパフォーマンスやメモリ要件に影響を与えません。DIコンテナのパフォーマンスが役割を果たす可能性がありますが、Nette DIの場合、コンテナは純粋なPHPにコンパイルされるため、アプリケーション実行時のオーバーヘッドは基本的にゼロです。
コードを記述する際には、依存関係を受け入れるコンストラクタを作成する必要がある場合があります。以前は時間がかかる可能性がありましたが、最新のIDEとコンストラクタプロパティプロモーションのおかげで、現在は数秒の問題です。ファクトリは、Nette DIとPhpStorm用プラグインを使用してマウスをクリックするだけで簡単に生成できます。 一方、シングルトンや静的アクセスポイントを記述する必要はなくなります。
DIを使用する適切に設計されたアプリケーションは、シングルトンを使用するアプリケーションと比較して、短くも長くもないと結論付けることができます。依存関係を扱うコードの部分は、個々のクラスから抽出され、新しい場所、つまりDIコンテナとファクトリに移動されるだけです。
レガシーアプリケーションをDIに書き換える方法は?
レガシーアプリケーションからDependency Injectionへの移行は、特に大規模で複雑なアプリケーションの場合、困難なプロセスになる可能性があります。このプロセスに体系的にアプローチすることが重要です。
- Dependency Injectionに移行する際には、チームのすべてのメンバーが使用される原則と手順を理解することが重要です。
- まず、既存のアプリケーションの分析を実行し、主要なコンポーネントとその依存関係を特定します。どの部分をリファクタリングし、どの順序で行うかの計画を作成します。
- DIコンテナを実装するか、さらに良いのは、Nette DIなどの既存のライブラリを使用することです。
- Dependency Injectionを使用するように、アプリケーションの個々の部分を徐々にリファクタリングします。これには、依存関係をパラメータとして受け入れるようにコンストラクタまたはメソッドを変更することが含まれる場合があります。
- 依存関係を持つオブジェクトが作成されるコード内の場所を変更して、代わりに依存関係がコンテナによって注入されるようにします。これにはファクトリの使用が含まれる場合があります。
Dependency Injectionへの移行は、コードの品質とアプリケーションの長期的な保守性への投資であることを忘れないでください。これらの変更を行うのは困難な場合がありますが、結果は、将来の拡張と保守に対応できる、よりクリーンで、よりモジュール化され、テストしやすいコードになるはずです。
なぜ継承よりもコンポジションが優先されるのですか?
変更の影響を心配することなくコードを再利用するために、継承の代わりにコンポジションを使用する方が適切です。したがって、あるコードの変更が他の依存コードの変更を必要とすることを心配する必要がない、より緩やかな結合を提供します。典型的な例は、コンストラクタ地獄と呼ばれる状況です。
Nette DIコンテナをNette以外で使用できますか?
もちろんです。Nette DIコンテナはNetteの一部ですが、フレームワークの他の部分から独立して使用できるスタンドアロンライブラリとして設計されています。Composerを使用してインストールし、サービスを定義する設定ファイルを作成し、数行のPHPコードを使用してDIコンテナを作成するだけです。 そして、すぐにプロジェクトでDependency Injectionの利点を活用し始めることができます。
コードを含む具体的な使用方法は、Nette DIコンテナの章で説明されています。
なぜ設定はNEONファイルにあるのですか?
NEONは、アプリケーション、サービス、およびそれらの依存関係を設定するためにNette内で開発された、シンプルで読みやすい設定言語です。JSONやYAMLと比較して、この目的のためにはるかに直感的で柔軟なオプションを提供します。NEONでは、Symfony&YAMLではまったく記述できないか、複雑な記述によってのみ記述できる関連性を自然に記述できます。
NEONファイルの解析はアプリケーションを遅くしませんか?
NEONファイルは非常に高速に解析されますが、この点はまったく重要ではありません。理由は、ファイルの解析はアプリケーションの初回実行時に一度だけ行われるためです。その後、DIコンテナのコードが生成され、ディスクに保存され、それ以降のリクエストごとに実行され、追加の解析を行う必要はありません。
これは本番環境での動作方法です。開発中は、開発者が常に最新のDIコンテナを持つように、内容が変更されるたびにNEONファイルが解析されます。前述のように、解析自体は一瞬の問題です。
自分のクラスから設定ファイル内のパラメータにアクセスするにはどうすればよいですか?
ルールNo.1:渡してもらうを覚えておきましょう。クラスが設定ファイルからの情報を必要とする場合、その情報にアクセスする方法を考える必要はありません。代わりに、単純にそれを要求します – たとえば、クラスのコンストラクタを介して。そして、設定ファイルで受け渡しを行います。
この例では、%myParameter%
はパラメータmyParameter
の値のプレースホルダであり、クラスMyClass
のコンストラクタに渡されます:
# config.neon
parameters:
myParameter: Some value
services:
- MyClass(%myParameter%)
複数のパラメータを渡す場合やautowiringを利用したい場合は、パラメータをオブジェクトにラップすることをお勧めします。
NetteはPSR-11: Container interfaceをサポートしていますか?
Nette DIコンテナはPSR-11を直接サポートしていません。ただし、Nette DIコンテナとPSR-11 Container Interfaceを期待するライブラリまたはフレームワークとの間で相互運用性が必要な場合は、Nette DIコンテナとPSR-11の間のブリッジとして機能する単純なアダプタを作成できます。