DI(依存性の注入)×AOP(アスペクト指向)の常識:企業システムの常識をJBossで身につける(3)(2/4 ページ)
企業向けアプリケーションのさまざまな“常識”をJavaのオープンソース・フレームワーク群である「JBoss」から学んでいきましょう。企業システムを構築するうえでの基礎となる知識をリファレンス感覚で説明していきます。初心者から中堅、ベテランまで大歓迎!
外部の設定ファイルに設定したインスタンスを「注入」する「DIコンテナ」
では、営業部クラスや人事部クラスは存在しなくても影響は受けないのでしょうか? 確かに改良を加えたことにより、会社クラスと営業所クラスの間の依存関係は改善できました。しかし、実際には実行クラスで営業部クラスや人事部クラスのインスタンスを生成する必要があるため、実行クラスでは影響があります。このインスタンス生成処理も省いてしまえばいいのですが、そうすると会社クラスに部署のインスタンスを渡すことができず、プログラムとして成立しません。
このようなことを解決する方法として、「依存性の注入」という方法が出てきます。本文でも説明しましたが、「プログラムの実行時に外部から依存性を注入すればいい」という考えです。このサンプルの場合は、必要な部署のインスタンスをプログラムの実行時に生成するイメージです。
では、どのように外部からインスタンスを注入するのでしょうか? 具体的な方法を説明すると、「外部の設定ファイルにインスタンスを設定し、対象のプログラムに設定したインスタンスを注入する」のです。
この一連の動作を仲介するソフトウェアを「DIコンテナ」といいます。プログラムの起動時に、DIコンテナが動的に対象のプログラムに依存の注入を行います。以下の図は、DIコンテナのイメージです。
このような「依存性の注入」の考え方によって、設定ファイルを修正するだけでオブジェクトが切り替えられ、クラス間の依存をなくすことができます。サンプルで、営業クラスから部署インターフェイスに修正したのは、オブジェクトの切り替えを行うことやクラスとの依存関係をインターフェイスのみに抑える必要があったからです。
「依存性の注入」を行う3つの方法
DIの仕組みを説明したところで、実際に「依存性の注入」を行う方法を紹介します。「依存性の注入」には、以下の3つがあります。ここでは、新しく会社クラスを作成しました。
- 【1】コンストラクタインジェクション
DIコンテナが、クラスのコンストラクタを利用してインジェクションを行う
/** * 会社クラス */ public class Company { Department department; public Company(Department department) { this.Department = department; } }
- 【2】セッターインジェクション
DIコンテナが、プロパティに対応したセッターよりインジェクションを行う
/** * 会社クラス */ public class Company { Department department; public void setDepartment(Department department) { this.Department = department; } }
- 【3】メソッドインジェクション
DIコンテナが、任意のメソッドよりインジェクションを行う
/** * 会社クラス */ public class Company { Department department; public void set(Department department) { this.Department = department; } }
“自動的に”注入
これらのプログラムは、基本的なJavaの構文で書かれており、特に懸念すべき個所はありません。では、どのように「依存性の注入」が行われるのでしょうか? DIコンテナは設定ファイルを読み込んで、自動的に、インスタンスを注入してくれる仕組みになっています。設定ファイルには、どのクラスのコンストラクタやメソッドなのかを記述するだけでいいのです。
このように「依存性の注入」にはさまざまな方法があるので、用途に合わせて開発を行う必要があります。
「依存性の注入」のメリット・デメリット
では、このような「依存性の注入」には、どのようなメリットがあるのでしょうか? それは、「オブジェクト間の依存関係を抑えることで、オブジェクトの保守性や拡張性を向上できる」ということです。また、テスト用のモックオブジェクトを作成して、コンテナより切り替えることで単体テストが行いやすいことが挙げられます。
しかし、DIにはデメリットもあります。「依存性を設定する外部のファイルが肥大化して管理することが難しくなる」ことや、「Javaのインターフェイスを理解していないと、実装が困難である」ことなどが挙げられます。設定ファイルの肥大化については、下記ソフトウェア/フレームワークでは、アノテーションなどを使って改善する傾向にあります。
DIコンテナはいろいろある
このようにDIを使用するに当たっては、DIを理解して開発を行うことが非常に重要なポイントです。実際に、DIコンテナを搭載しているソフトウェア/フレームワークには、「Spring」「Seasar2」「Google Guice」や、前回の連載で説明した「JBoss Seam」などが存在します。これらは一体どのようなものなのでしょうか? それらを説明する前に、今度はAOPについて説明したいと思います。
いまさら聞けない「AOP」「アスペクト指向」入門
「AOP」とは、Aspect Oriented Programmingの略称で、「アスペクト指向(プログラミング)」と呼ばれています。これは、「横断的関心事の分離」ともいわれます。これもDIと同様に、初めて聞く人には何のことか見当が付かないと思います。「アスペクト指向(プログラミング)」や「横断的関心事の分離」とは、一体どういうことなのでしょうか?
そもそも「横断的関心事」とは
まず、この「アスペクト」という言葉から考えてみます。この言葉には、「様相」「側面」「視点」といった意味があります。これらの言葉より、「アスペクト指向(プログラミング)」を考えてみると、「プログラミングを、別の視点や側面から考える」といったイメージです。この意味を簡単に説明すると、オブジェクト指向プログラミングでは、カバーできない個所を補うことを指しています。では、ここでいう「カバーできない」とは、どのようなものなのでしょうか?
そもそもオブジェクト指向とは、「オブジェクト同士の相互作用からシステムの動作や構造を設計していく」という考え方です。抽象化によるクラス設計を行い、オブジェクトの再利用や拡張性が向上できます。しかし、Javaのオブジェクト指向のクラス設計を行ったとしても、どうしても個々のクラスに重複コードを書かなければならないことがあります。
具体的な例を挙げると、クラスに散在するロギング処理があります。
このように個々のクラスに処理が散在してしまう理由は、Java言語において多重継承が許可されいないからです。上記の例でいうと、ロギング処理を親クラスにまとめて、そのクラスを継承するという方法がとれません。仮にJavaが多重継承を許可したとしても、複数のクラスのメソッドの中に、散在する処理をひとまとめにするのは困難です。
そのため、呼び出されるクラスやメソッドのログを確認したい場合は、どうしても上記のように個別にログを実装して確認する必要があます。このように、処理がオブジェクトにまたがることを「横断的関心事」と呼んでいます。プロジェクトにおいて、このような処理が増えていくと、コードの可読性やメンテナンス面が低下します。
インタセプタ/アドバイスとは
アスペクト指向プログラミングでは、この「横断的関心事」を「分離」することを目的としています。これにより、既存のコードに手を加えなくてもプログラム中に散在する特定の機能を持った部分を書き替えられます。具体的には、以下のようなイメージです。
上記のように切り出されたモジュールを、アスペクト指向では「インタセプタ(Interceptor)」または「アドバイス(Advice)」と呼びます。また、「インタセプタ」が処理を仕掛ける個所を、「ポイント・カット(Point Cut)」と呼びます。また、この「ポイント・カット」を呼び出すことを「ジョイント・ポイント(Jointpoint)」といいます。
これらの関連するポイント・カットとインタセプタを設定ファイルに記述することで、各クラスはアスペクトを実装する必要がありません。設定ファイルに記述されているオブジェクトが実行されることにより、上記の例では、ログが出力されるようになります。各クラスでは、ログなどの設定を意識する必要はありません。
ロギングやトランザクション管理、例外処理を「アスペクト」に
ログ以外の処理に関してもトランザクション管理や例外処理などにも活用できます。このように、ロギングなどで分散した処理をひとまとめにしたものを、「アスペクト」と呼びます。AOPにより、元のソースコードに手を加えることなく新たな処理を追加(Advice)でき、プログラムコードの重複を省きます。結果として、オブジェクト指向プログラミングでは、カバーできない個所を補うことができるのです。
DIとAOPの説明が終わったところで、次ページからは、これらを代表するDI×AOPのソフトウェア/フレームワークを簡単に紹介します。
Copyright © ITmedia, Inc. All Rights Reserved.