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