>>前編

DI

 DIコンテナがどのような仕組みでDIを実現しているのか見ていこう。

 親クラスAに子クラスBを引き渡す場合で考える。まずDIコンテナには(1)クラス間の関係(依存性)を記述した設定ファイルを登録しておく(図1)。代表的なDIコンテナであるSeasar2やSpring Frameworkでは,この設定ファイルはXMLで記述する。そして,(2)クラスAを取得したいという要求をDIコンテナが受け付けると,(3)DIコンテナがクラスAに関する設定ファイルを読み取る。(4)設定ファイルに従って,クラスAと,関連するクラスBを生成する。(5)インタフェースを介して,クラスBをクラスAに引き渡す,といった仕組みである。もしクラスAに引き渡すクラスを削除したい場合は,「クラスBを生成する,クラスBをクラスAに引き渡す」といった設定を,設定ファイルから削除すればよい。

図1●DIコンテナの仕組み
図1●DIコンテナの仕組み
クラスが要求されると,そのクラスに関連する設定ファイルを検索する。設定ファイルがあれば,クラスの生成やクラスの引き渡しといった設定内容を実行する
[画像のクリックで拡大表示]

DIでテストを効率化

 DIはプログラム開発に多くのメリットをもたらす。Seasar2の開発者である電通国際情報サービスの比嘉康雄氏(事業推進本部 開発技術センター 統括マネージャー)は,「DIを導入することでテスト効率が上がる」と話す。クラスの単体テストを行う場合,テスト対象のクラスが受け取れるインタフェースを実装した「テスト用のクラス」を用意すれば,簡単にテストが実行できる。「一部のクラスが揃わなくてもテストができるので,テスト・ファーストでのプログラム開発にも向く」(日本IBM 東京基礎研究所 副主任研究員 立堀道昭氏)という意見もある。テスト・ファーストとは,プログラム開発前にテスト内容を決めて,テスト内容をパスするように開発し,単体テストを実施する開発手法である。

 保守性が向上するという意見も多い。システム開発を手掛けるスティルハウスの吉川和己氏は,「クラスを複雑なEJBではなく,シンプルなPOJO(Plain Old Java Object)にできる。その結果,何をするプログラムなのか見通しが良くなる」と語る。

 また,DIは素早い変化が求められるシステムでの利用に向く。DIコンテナを勤怠管理パッケージで利用しているNTTデータ セキスイシステムズの古沢英樹氏(システム開発統括部 大阪開発グループ)は,「勤怠管理の機能要件は顧客ごとに異なる部分が多い。機能変更が容易であるDIコンテナの仕組みを使えば,カスタマイズ開発の期間を短くできる」と語る。NRIの松野洋希氏(オープンソースソリューションセンター テクニカルエンジニア)も,「証券の手数料計算は,一日定額になったり取引ごとの計算になったりと,頻繁に変更される。開発中の証券システムはDIコンテナを採用しているので,こうした変更にすぐに対処できる」とメリットを認める。

DIを設定する手間を軽減

 DIコンテナを使うには,設定ファイルが必須。そのため,記述の手間や間違いがないかチェックする手間が発生する。DIコンテナには,こうした手間を軽減する機能もある。(図2)。その一例としてSeasar2では,「AutoRegister機能」という機能を用意している。この機能を使えば,設定ファイルに検索条件を書いておくだけで該当するクラスをパッケージから見つけ出して登録してくれる。例えば図2上の設定ファイルは,図1内の設定ファイルと同様の内容を実行するもの。「Impl」を含むクラスを登録するように記述しておくことで,MessageImplクラス(クラスB)と,MessageClientImplクラス(クラスA)を探し出して登録している。ただし,この設定ファイルにはクラスBをクラスAに渡す記述がない。これは,インタフェースXを持つクラスBと,インタフェースXを持つクラスを受け取れるクラスAの組み合わせを,DIコンテナが自動で判断するためである。

図2●DIコンテナには,設定ファイルの記述やデバッグを支援する機能がある
図2●DIコンテナには,設定ファイルの記述やデバッグを支援する機能がある
検索条件に合ったクラスを探して登録したり,設定ファイルの構文ミスをチェックしたりする
[画像のクリックで拡大表示]

 また記述ミスを防ぐために,Seasar2は設定ファイルをチェックする機能を用意している(図2下)。この機能は「kijimuna(キジムナー)」というソフトウエアで提供され,開発環境であるEclipseのプラグインとして利用する。設定ファイルの中で,「component」を「conponent」と書くスペルミス(“n”)やタグの閉じ忘れ,存在しないクラスの指定といった間違いを開発者に知らせる。

AOP

 AOPの実現手段には二つの方法がある。「静的なAOP」と「動的なAOP」である。

 AspectJは静的/動的の両方とも可能だ。静的なAOPでは,ベースになるクラスと,「アスペクト」と呼ばれる追加する機能を持ったクラスを用意する(図3上)。アスペクトの2~3行目のpointcutで始まる部分は,ベースになるクラスのどの場所に機能を追加するかを定義しており,ポイントカットと呼ぶ。3行目が具体的な条件で,「MessagingクラスのprintMessageメソッドが記述されている場所に追加する」ことを定義している。これはジョインポイントと呼ぶ。続いて4行目と5行目のbeforeで始まる部分には実行内容を記述する。これはアドバイスと呼ぶ。あとはこの二つのクラスをAspectJの専用コンパイラでコンパイルすると,機能が追加されたクラスが生成される。実行前にコンパイルするので,静的なAOPと呼ばれる。

図3●AOPの実現方法は二種類がある
図3●AOPの実現方法は二種類がある
コンパイル時に機能を追加する「静的なAOP」と,実行時に追加する「動的なAOP」がある
[画像のクリックで拡大表示]

DIコンテナでAOPも実行

 動的なAOPは,ベースになるクラスと,アスペクトをコンパイルしておき,ベースとなるクラスの実行時にアスペクトを追加することから動的なAOPと呼ばれる。動的なAOPの代表例が,DIコンテナを使ったものだ。ベースになるクラスのどこに追加するかは,DIコンテナの設定ファイルの中にXMLで記述する。Seasar2の場合,まずベースになるクラスを生成するコードを記述する。その後に〈aspect〉タグで,アスペクトを記述する。

 Seasar2のコミッタであるエスエムジーの小森裕介氏(システムズ コンサルティング ディヴィジョン テクニカル コンサルタント)は,「DIコンテナがあれば,DIとAOPの両方が使える。専用コンパイラなしにAOPを実現できるのが魅力だ」と語る。

設計時にAOPを取り入れる

 AOPは,先に挙げたロギング機能や権限チェック機能のような「非機能要件」の実装に使われることが多い。しかし,東京工業大学の千葉滋氏(大学院 情報理工学研究科 助教授)は,「AOPの適用範囲は非機能要件だけではない。機能要件にも使える可能性は十分ある」と語る。実際,機能要件でAOPを検討する取り組みもある。ユースケースの作成段階からAOPを意識して設計する手法だ。この手法を使えば,「ユースケースが頻繁に追加,変更されるシステムでも,拡張性や保守性が向上する」(国立情報学研究所 助手 鷲崎弘宜氏)という。

 一例を示そう。商品購入のWebサイトを構築するケースだ(図4)。このWebサイトは新規会員登録と抽選を行う仕組みが必要だとする。このそれぞれを表すユースケースからどのようなクラスを実装すべきかを考える。

図4●AOPを設計時から意識して取り入れる試みも始まっている
図4●AOPを設計時から意識して取り入れる試みも始まっている
クラスのつながりを図示し,一つのクラスに存在する複数の機能や,複数のクラス間で重複する機能を洗い出す。それらの機能をAOPで実装することで,拡張性や保守性を高められる
[画像のクリックで拡大表示]

 新規会員登録では,「会員登録画面」と「会員情報追加と会員へのメール通知」,「会員リスト」というクラスで構成される。一方のプレゼント抽選では,「抽選画面」と「抽選と会員へのメール通知」,「当選者リスト」の三つで構成される。それぞれの構成要素を表に書くと,問題があることが分かる。会員情報追加および抽選と,メール通知機能が分離できていない。また二つのユースケースで,メール通知機能が重複している。このまま実装すると,メールで通知する機能が不要になったとき,それぞれのクラスで修正が必要になってしまう。メール通知機能を二重に開発するムダもある。そこで,メール通知機能をAOPとして実装する。

 このように,AOPは機能要件にもメリットがある。ただし,「AOPで追加する機能が多い場合は,ベースとなるクラスに記述しているコードを読んでも,実行内容が分かりにくい」(クロノス 第二開発部サブリーダー 阪田浩一氏)という指摘があるように,すぐに機能要件にAOPを適用できるわけではない。とはいえ,簡単に機能を追加できるAOPは強力。今後は機能要件でAOPを使う試みが増えるだろう。