「DI(Dependency Injection)」および「AOP(Aspect Oriented Programming)」と呼ばれる技術が注目を集めている。これらはオブジェクト指向プログラミングにおけるプログラムの単位であるクラスを,互いに結び付ける新たな技術である。システムへの機能変更ニーズが高くなり,さらに開発期間が短くなっている開発の現場において,開発の効率化や品質向上を実現する新たな手段として期待されている。まずはオブジェクト指向プログラミングにおける課題を明らかにし,DIやAOPがそれらをどう解決できるのかを見よう。

DIでクラスを容易に付け外す

 オブジェクト指向プログラミングの一つ目の課題として,「変更時にクラスの修正が必要になる」ことがある。そもそも,オブジェクト指向で開発したプログラムは,オブジェクト指向ではないプログラムと比べ,機能の削除や変更が容易であることが特徴だ。オブジェクト指向で開発したプログラムは,あるクラスが別のクラスを呼び出すことで,ツリー構造になってアプリケーションを構成している。機能の一部を削除したい場合は,その機能を持つクラスを削除すれば済む。修正部分を限定的にしているので,この仕組みで十分なように思える。しかし,さらに開発効率を上げるためには,見逃せない課題がある。それは,子クラスを削除する際に,親クラスのコードを修正する必要があることだ(図1)。

図1●DIを使えば,クラスの付け外しが容易になる
図1●DIを使えば,クラスの付け外しが容易になる
従来のオブジェクト指向で開発したプログラムでは,子クラスを削除または別のクラスに変更する際に親クラスのコード修正が必要になるという課題があった。DIを使えばコード修正が不要になるため,クラスの付け外しが簡単になる
[画像のクリックで拡大表示]

 例えば親クラスAが子クラスBを呼び出している場合,クラスAには「クラスBを生成して呼び出す」というコードが記述されている。ここで不要になったクラスBを削除したとする。クラスAの「クラスBを生成して呼び出す」コードを削除せずに実行すると,クラスBが存在しないのでエラーになる。つまりクラスBを削除するときは,クラスAの修正が必要になる。これは,親クラスAが子クラスBを必要としていることを意味し,「依存性」と呼ぶ。この課題を解決するのがDIである。DIはこの依存性の記述をクラスの中から追い出し,まるでボタンを外すように,削除や変更を可能にする。

機能を後から楽に追加

 オブジェクト指向プログラミングの二つ目の課題は,「機能の追加時にコード修正が必要になる」である。

 ロギング機能や権限チェック機能など,多くのプログラムで必要になる機能を追加する手間は煩雑だ(図2)。こうした機能を複数のクラスに追加する場合,ロギング機能や権限チェック機能を持つクラスを呼び出すコードを,各クラスにそれぞれ書かなくてはならない。追加先が多い機能だけに,コードの記述に手間がかかるし,間違いが発生する可能性が高くなる。この課題を解決できるのがAOPである。AOPはクラスをコンパイルまたは実行するタイミングで機能を追加できる技術。元のクラスには何も手を加えない。追加した機能を取り外したい場合も,元のクラスを修正する必要はない。

図2●AOPを使えば,コードの修正なしに後から機能を追加できる
図2●AOPを使えば,コードの修正なしに後から機能を追加できる
ロギング機能や権限チェック機能など,多くのプログラムに共通する機能を追加する場合に有効。記述の手間が減る,記述の間違いが発生しにくいといったメリットがある
[画像のクリックで拡大表示]

 NTTデータ セキスイシステムズの加藤穣氏(システム開発統括部 大阪開発グループ 主任)は,「暗号化通信や権限チェックといった,たびたび登場する機能を簡単に追加できるので,開発工数が少なくて済む」とAOPのメリットを語る。同社は勤怠管理パッケージ「JobCubic Time」にAOPを取り入れている。AOPを適用していない以前のバージョンでは,機能を後から追加する際の手間がかかっていた。

 AOPは,ロギング機能など,機能要件そのものではない「非機能要件」の実装に使われることが多い。そんな中,ユースケースと併用して機能要件でも使えないかという試みも始まっている。

DI/AOPでクラスをひも付け

 ここまでオブジェクト指向プログラミングの課題と,DIとAOPによる解決方法を説明してきた。クラスAからクラスBを呼び出す際,DIとAOPはいずれもクラスAの中に「クラスBを生成して呼び出す」というコードを記述する必要がなかった。どちらも,クラス間の境界線を明確にし,クラスの付け外しを簡単にする技術だと言える。ただし,両者の実装方法は異なるので,それぞれの特徴を押さえておこう(図3)。

図3●DIとAOPはいずれもクラス間の境界線を明確にするが,実装方法が違う
図3●DIとAOPはいずれもクラス間の境界線を明確にするが,実装方法が違う
DIはインタフェースを使ってクラス同士を結び付ける。AOPは任意のメソッドを目印にして,ベースになるクラスに機能を追加する
[画像のクリックで拡大表示]

 DIでは,クラスAとクラスBをひも付ける作業をDIコンテナというミドルウエアが行う。あらかじめクラスAとクラスBの依存性をDIコンテナに登録しておけば,クラスの実行時に依存性(Dependency)が注入(Injection)される。ひも付けには,インタフェースを用いる。クラスBにはインタフェースを実装しておき,クラスAはそのインタフェースを持ったクラスを引数として受け取れるようにしておく。インタフェースが一致していれば,どのようなクラスでもクラスAに渡せるので,機能変更も容易だ。

 一方のAOPは,クラスA(元になるクラス)にクラスB(追加したい機能)を挟み込むイメージだ。そのトリガーとなるのが,元になるクラスに含まれるメソッドである。例えばAspectJ(Javaを拡張させたAOPを実装するための開発言語)というJavaを拡張した言語でAOPを実現する場合,“追加したい機能”と,「ベースになるクラスにあるメソッドAが記述されている場所」といった,機能を追加する“条件”の二つを記述したクラスを作成しておく。このクラスと,ベースのクラスを専用のコンパイラでコンパイルすると,機能が追加された新しいクラスが作成される。

 ここまでがDIとAOPの概略である。後編では,DIとAOPの詳しい仕組みと動向を解説していく。

>>後編