DI一般について
引きつづきDI(Dependency Injection)について。
急がば回れというか、さすがに個別のプロダクトや開発パターンの問題になると、専門書はないか、あってもやたらと難しいわけです。
そういうわけで、もはや断片的記述であろうとしかたあるまいと判断して、一方でDI機能を提供するフレームワークの入門書を図書館で取り寄せつつ、以前から読んでみたいと思っていた、『軽快なJava』をAmazonで購入しました。
軽快なJava―Better,Faster,Lighter Java
- 作者: ブルース・A.テイト,ジャスティンゲットランド,Bruce A. Tate,Justin Gehtland,岩谷宏
- 出版社/メーカー: オライリージャパン
- 発売日: 2004/09
- メディア: 単行本
- 購入: 1人 クリック: 18回
- この商品を含むブログ (48件) を見る
依存性の注入とは
さて、DIの一般的モデル、DIの意図するところ、思想とは何なのかということです。
前掲書157ページには、こうあります。「個々のサービスやリソースを、インタフェイスとして扱えます。あるビーン〔Bean。部品化されたプログラム〕があるリソースを使うとしたら、そのビーンはベースクラスの継承ではなくインタフェイスに頼ります。そしてコンフィギュレーションファイルに従って、そのインタフェイスを実装している何らかのビーンを使います」。
つまりあるクラスAがあり、このクラスのメソッド内では、リソース──これはDBから取得されるようなデータかも知れませんし、何らかの機能を提供するAPIかもしれません──を使用するために別のあるクラスBを必要とします。つまり前者は後者に依存しています。
クラスではなくインターフェースを
ここでDIモデルでは、クラスAのコード内では、クラスBやクラスBのサブクラス──いずれにしてもクラスBを祖先に持つ何らかのクラス──を指定し、インスタンスを取得し、メソッドを呼び出す、といったことはしません。
クラスBはクラスAのコード内ではインターフェースで指し示されるのです。クラスAのコードは、クラスB(インターフェースB)の実装のインスタンス取得を、DIフレームワークのインジェクター(Injector)に委譲します。インジェクターは要求に応じて、しかるべくクラスB(インターフェースB)の実装のインスタンスを返すのです。Springなどのフレームワークでは、インジェクターの役割は、アセンブラー(Assembler)と呼ばれるコンポーネントが担います。
そしてクラスB(インターフェースB)の実装のインスタンスが求められたときに、実際にリソース側のどの実装が使用されるのかを決定するのはコンフィギュレーションファイルの役目です。あるいはGuiceであれば、これがModuleクラスの役目です。
ここでインジェクターの存在と同様に重要なのは、クラスAは何らかの他のリソースへの依存の表現に、「ベースクラスの継承ではなくインタフェイス」を用いるということです。
これによりクラスBもしくはインターフェースBは、継承チェーンを通じたある祖型クラスへの束縛やパッケージへの所属などから解放され、実装はもちろん出所・所属を問われない何ものかになることができます。
これは結局のところPython言語における“ダックタイピング”の思想に通じるものでしょう。
家電の例
前掲書ではすぐあとに家電製品のプラグとコンセントの比喩を挙げています。「…ここで家電製品を家庭で使うときのプラグについて考えてみましょう。必要な機器(==必要なサービス)を差し替えて使うことのできるプラグは、まさにインタフェイスです」。
この例ははっきり言って話を混乱させています。家電のプラグという規格=インタフェイスの実装を、コンセントに差し込んだからといって、その差し込み口の側がそれらの家電から「サービス」を受けるわけではありません。事実は逆です。
より適切な比喩としては、むしろ家電の側が、自身のプラグを差し込めるような規格=インターフェースを持つコンセントであればどんなコンセントからでも電力というサービスを得られる、ということでしょう。
そしてプラグの形状やそこから得られる電気の電圧は、国や地域によりさまざまです。しかし汎用アダプタを使用することで、これらの差異のある部分は吸収できます。
DIの役割はまさにここにあります。DIを使用して構築されたアプリケーションは、こうした汎用アダプタを常に付けっぱなしにしている家電なのです。そしてこのアダプタの接続先を変えるのが、コンフィギュレーションファイルやModuleクラスです(前掲書の例では「汎用アダプタのマニュアル」)。