EJB3.0がDIを取り込んだことに開発者達が驚いた理由は,もともとDIとは「現状のJ2EE(Java2 Platform, Enterprise Edition)は複雑すぎる」との批判を背景として広がった技術だったからだ。J2EEへの批判から生まれた「反・主流派」だったはずのDIという技術が,「主流派」であるJ2EEの中核技術であるEJBに取り込まれたわけである。最近滅多に聞かなくなった「正-反-合」という言葉を思い出すような事件ではないか。

 J2EEとは,意地悪な見方をすれば「複数のフレームワークの寄せ集め」である。しぜん,習得すべき技術も多くなる。特にEJB(Enterprise JavaBeans)は,現行版のEJB2.1仕様書の厚みが600ページ以上あることから分かるように膨大な積み重ねの上に成り立つ技術体系である。これらのフレームワークを搭載したJ2EEアプリケーション・サーバーが「重量級」のソフトとなるのも当然のことである。

 そしてDIとは,J2EEの複雑さを救う救世主として期待されている技術なのである。J2EEの分野でDIに注目が集まる理由は,重量級のソフトを使わずに同等の開発が可能となるからだ。DIとは,EJB技術に適用することによって,そのメリットが際だつという性格を持っていたのである。

 なお,DIそれ自体は汎用的な技術で,EJB以外の分野でもメリットを発揮する。またJava技術と結びついた技法というわけではない。実際,Spring Frameworkの.NET版である「Spring.NET」や「NSpring」なるものも登場している。ただ,Java言語が現代的なオブジェクト指向言語として最も普及していることや,EJBとの対比でDIコンテナが注目されたという経緯もあって,DIに関する開発事例はJava技術分野のものが多い。

DIコンテナの技術的なポイントとは

 DI(Dependency Injection,依存性注入)の命名者は,ソフトウエア設計のコンサルタントとして名高いMartin Fowler氏(ThoughtWorks社)である。とはいってもFowler氏がDIを発明したわけではなく,すでに存在していた最新のソフトウエア(picoContainer,Spring Framework,Avalon)で使われていた設計手法に対して,改めて名前を付けたのである。

 DIの祖先にあたるアイデアは古くからあり「IoC(Inversion of Control,制御の反転)」と呼ばれていた。Fowler氏はIoCの概念を整理,範囲を限定して「DI(Dependency Injection)」という言葉を定義した。今では「DI」の方が通りが良いようである。例えばSpring Frameworkのドキュメントは当初「IoC」という用語を使っていたが,後に「DI」に改めた。

 DIコンテナの本質的なメリットは何か。Martin Fowler氏は「設定を利用から分離する」ことにある,と述べている(関連情報翻訳記事)。これをもう少しかみ砕くと次のようになる。

・ソフトウエアの階層をきれいに分離した設計が容易になる
・コードが簡素になり,開発期間が短くなる
・テストが容易になり,「テスト・ファースト」による開発スタイルを取りやすくなる
・特定のフレームワークへの依存性が極小になるため,変化に強いソフトウエアを作りやすくなる(=フレームワークの進化や,他のフレームワークへの移行に対応しやすくなる)

 DIを実現するメカニズムの概要は,オブジェクト相互の依存性をプログラム外部に記述して,実行時に結合することである。これにより,オブジェクト間の独立性が高まる。「コンポーネント」と「フレームワーク」といった階層型の設計をする場合に特に有用となる。

排除すべき「依存性」の正体とは

 DIの詳細を説明する前に,まず「ソフトウエアの依存性」について,簡単に復習しておこう。

 オブジェクトは,単体で動かすものではなく,他のオブジェクトと組み合わせてはじめて機能するものである。特に,「フレームワークやコンテナ(=オブジェクトを動作させる基盤)」と「コンポーネント(当該フレームワークの存在を前提としたオブジェクト)」という関係では,強い依存性が発生する。

 Java言語ではオブジェクトは「Javaクラス」として記述する。ここで,「A」というJavaクラスのソースコード中に,「B」を呼び出すコードが含まれていたとしよう。このコードは,「AがBを生成し,そのメソッドを呼び出す」という内容になる。生成するためには,AはBを知っていなければならない。つまり,Aを実行するには同時にBが必要となる。これが依存性の正体である。

 Aの実行時にBが存在しないとどうなるか。エラーとなる。Aを動作させてテストする場合には, Bが必要となる。当たり前の話である。

 ところが,この「B」が極端に重たいオブジェクトだったとしたらどうなるだろうか。例えば,現状のEJBコンポーネントは,EJBコンテナが用意するクラスを継承し,そのメソッドを必ず呼び出す。EJBコンテナとは,EJBコンポーネントを動作させるための環境で,J2EEアプリケーション・サーバーが搭載する機能群の一つ。トランザクション管理,リソース管理など多くの機能を実装した,「重量級」のソフトウエアである。

 つまり,EJBコンポーネントはEJBコンテナへの依存性を持つ。ところが,EJBコンテナ(すなわちJ2EEアプリケーション・サーバー)は,起動時間がかかるうえ多くのメモリーを消費する。EJBコンポーネントを実行するにはEJBコンポーネントをデプロイ(配備)する手続きも必要である。要するに,コンピュータ資源も,開発者の作業時間も余分に取られることになる。

 このため,EJBコンポーネントや,EJBコンポーネントを呼び出すクラスのテストは,開発者にとっては負担が多い仕事となる。その理由は「重量級」のEJBコンテナへの依存性があるためである。

依存性を実行時に「注入」する

図1●DIによる依存性の解消
 重量級コンテナへの依存性が問題であるなら,この「依存性」を解消できれば,より「軽量な」開発ができるのではないか? 先に説明したように,オブジェクトAがBに依存するとは,「Aのコードの中に,Bを生成してそのメソッドを呼び出すコードが含まれている」ということである(図1の(a)[拡大表示])。AがBを生成するには,A はBというオブジェクトを知っていなければならない。ここに依存性が発生する。

 依存性の正体がオブジェクトの生成のコードであるならば,この「悪者」となっているコードを消せないか? 具体的には,Bを生成するコード(=依存性)をA本体に含めず,その実行時に動的に「注入」できないか? この考え方が,依存性注入(Dependency Injection),すなわちDIなのである。