Top / Java関連 / JavaEE5 / JavaEE5におけるDI機能(1)

JavaEE5におけるDI機能

EJB3.0になってから限定的ですが、EJBコンテナがDI(Dependency Injection)機能を持つようになりました。
EJB3.0が持つDI機能について、備忘録的に残しておきます。

 

DI(Dependency Injection)機能とは

参考書としている[マスタリングJavaEE5]のDIの説明は秀逸で、キツネにつままれたような感じですが、
なんとなく、DI機能の意義が分かった気になりました。

 

その説明について、補足を入れながら、だらだらと解説したいと思います。

クラスの依存関係

アプリケーションは、いろいろな機能を持った数多くのクラスから構成されているが、
[クラスとクラスの結びつき方]、言い換えると[クラスからクラスの呼び出し方]を工夫すると、クラスの結びつき方が疎にすることができます。

 

クラスの呼び出し方は、以下のようなパターンがあります。

  1. Barオブジェクトを直接生成(new)する。
  2. Barクラスを実装とインタフェースに分離し、ファクトリメソッドを使って取得する。
  3. Barクラスを実装とインタフェースに分離し、DIを使用して取得する。
     
    ここでBarオブジェクトは、何かしら便利な機能を持つクラスと漠然と考えるとよいです。

直接作成する例

Mainクラスは、呼び出し用の簡単なクラスです。

package com.techch.usual;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("[ Main#main ] Start.");
        
        // Fooオブジェクトを作成する。
        Foo foo = new Foo();
        
        // ビジネスメソッドを呼び出す。
        String result = foo.doSomething("Hello");
        System.out.println("result = " + result);
        
        System.out.println("[ Main#main ] End.");
    }
}

次にFooクラスは、Barの機能を呼び出す簡単なクラスです。
Barクラスは、newによって直接インスタンスを作成しています。

package com.techch.usual;

public class Foo {

    public String doSomething(String arg) {

        System.out.println("[ Foo#doSomething] Start.");
        
        // Barオブジェクトを直接作成する。
        Bar bar = new Bar();
        
        // Barオブジェクトのビジネスメソッドを呼び出す。
        String result = bar.doSomething(arg);
        System.out.println("result = " + result);

        System.out.println("[ Foo#doSomething] End.");
        
        return result;
    }

}

最後にBarクラスですが、ビジネスロジックとして、パラメータの文字列に対し、墨付きカッコで括った文字列を返却します。

package com.techch.usual;

public class Bar {

    public String doSomething(String arg) {

        System.out.println("[ Bar#doSomething] Start.");
        
        // ここで何かしらのビジネスロジックを実装する。
        // ここでは、引数で渡された文字列に、墨付きカッコを付けた文字列を返却する。

        System.out.println("[ Bar#doSomething] End.");
        
        return "【" + arg + "】";
    }
}

このパターンでは、FooクラスとBarクラスが機能的に強く結び付いており、柔軟性に欠けています。
また、このように作成すると呼び出し先のクラス(Barクラス)の実装が終わらないと、呼び出し元のクラス(Fooクラス)のコンパイルを通すことができません。*1

インタフェースベースのプログラミング

このような問題を解決する手段として、Javaのオブジェクト指向言語としての最大の武器であるインタフェースを活用します。
つまり、呼び出し先のクラス(Barクラス)の実装とインタフェースを分離して、実装よりも先に、[Barクラスの呼び出し方]
つまりインタフェースを先に決めてしまいます。
呼び出し元のクラス(Fooクラス)では、インタフェースさえあれば、コンパイルを
通すことができます。
これにより、Fooクラスと、Barクラスの実装を並行して進めることが可能になります。

 

インタフェースベースのプログラミングモデルを取り入れることにより、クラスとクラスの依存関係を[疎]にすることができます。

 

このプログラミングモデルの最大の欠点は、クラス(実装)の他にインタフェース(宣言)が必要になるので、ファイル数が莫大な量になることです。
またそれに応じて、確実にコーディング量も増えます。
ただ、インタフェースのコーディングについては、ほとんどの場合、Javadocコメントのみの作成なので、コーディング自体は難しくはありません。

 

自分の経験上、こういった時間がかかる作業を若い方に任せると、異常なほどコーディングがはかどります。

 

一人でこれらの作業も実施した場合の約2.5倍の量のコーディングできます。
それだけこれらの面倒な作業は時間がかかるということなのでしょう。

 

また簡単な作業であっても、コードとして残るので、若い方にとっては自信になりますし、
トータルで2.5倍ものコードを作成できるという事実からそのありがたみは変えがたいものになります。

インタフェースベースのプログラミングモデルをしなかった場合

実際には、他チームの実装を待ってからコーディングするということはあり得ないですし、他の作業も盛りだくさんなため、このようにはなりません。

 

ですが、インタフェースベースのプログラミングを実施していないと、BarパッケージのMockを作る際に、Fooパッケージに手を加えるといった本末転倒なことが発生しがちです。
(で、本当のBarパッケージがリリースされたら、Fooパッケージを元に戻す。で、余計なところをいじってしまい、バグが発生する。ありがちなパターンですので、やってはなりません。)

Test01.png
 

インタフェースベースのプログラミングモデルを実施した場合

実際には、インタフェースベースのプログラミングを実施したとしても、単体テストを実施するには、Mockオブジェクトのような、
実体となるオブジェクトを作らない限り、デバッグするとことはできません。

 

また、このプログラミングモデルを使用した場合の最大の欠点は、思い込みによるプログラミング誤りに対処方法がないことです。
インタフェースをきっちり固めたとしても、あくまでもインタフェース上の話であって、そのメソッドの振る舞い方は、微妙に想定外の事項が発生します。
ただし、その違いについては、結合試験で担保するということになっています。

Test02.png
 

まだ、まだ続く。

最新の10件
2010-07-09 2010-07-08 2010-07-07 2010-06-29 2010-06-28
人気の20件
Counter: 372, today: 1, yesterday: 0

*1 確かに実体がない場合、コンパイルエラーになってしまいます。