気になる開発プロダクツ

第1回[前編] Guice 1.0 - GoogleからリリースされたDIフレームワーク

Guice(ジュースと読む)は、GoogleのエンジニアBob Lee氏(ブログ)などによって開発されたDIフレームワークです。バージョン1.0が2007年3月8日にApache License 2.0の下で公開されました(公開時のブログ)。実行環境としてJava 5が必要です。

DIとは依存性の注入(Dependency Injection)のことで、同じインターフェースを持つ具象クラスを、設定によって入れ替え可能にする方法を指し、IoC(Inversion of Control - 制御の反転)と呼ばれることもあります。これにより、たとえばテスト用のモッククラスと、実際の業務ロジックが組み込まれたクラスとの、必要に応じた入れ替えがしやすいというメリットがあり、システム開発の生産性を向上させる技術して注目されています。同様のしくみを持つものとして、Spring FrameworkPicoContainerNanoContainerS2Container(Seasar2)などがあります。

DIフレームワークは、あるインターフェースを持つクラスのインスタンスを生成する際、具象クラス(依存性)を注入する処理を行います。そのため、事前にインターフェースに対してどの具象クラスを対応させるかを設定しておく必要があります。フレームワークによって、設定をXMLファイルに記述するか、Javaクラスで実行するかが異なりますが、Guiceは後者に分類できます。つまり、GuiceはXMLファイルを利用しないDIフレームワークです。

Guiceのダウンロード

Guiceのバイナリモジュールはguice-1.0.zipですが、ファイル名はバージョンアップにより変更される可能性があります。これを展開すると複数のJARファイルやJavadocなどが生成されます。Guiceを用いたアプリケーションをコンパイルおよび実行する際は、展開されたJARファイルをCLASSPATHに加えておいてください。

ソースコードは、GuiceのWebサイトから「Downloads」をクリックしたあとの画面よりguice-1.0-src.zipをダウンロードできます。こちらもファイル名はバージョンアップにより変更される可能性があります。

それでは、実際にGuiceを味わうことにしましょう。

基礎編

ここでは基礎編として、インターフェースと具象クラスとを対応させるDIの設定から、インスタンスを生成するまでの一連の流れを紹介します。

DIの設定

GuiceによるDIの設定は以下の手順で行います。これらは他のDIフレームワークとほぼ同様で、それほど特殊な方法というわけではありません。

  • 1) インターフェースを作成する
  • 2) 具象クラス(依存性のもと)を作成する
  • 3) 1)と2)とを対応させる(DIの設定をする)モジュールを作成する
  • 4) 2)を実行するためのクラスを作成する

1) インターフェースを作成する

まず、例としてPlayインターフェースを作成します(リスト1)。これにはplay()というメソッドの宣言を記述しておきます。メソッドの中身は2)で作成する具象クラスに実装します。

リスト1 インターフェースの作成

public interface Play  {
    // メソッドの中身は具象クラスで実装する
    void play();
  }

2) 具象クラス(依存性のもと)を作成する

1)で作成したインターフェースを実装した具象クラスを作成します(リスト2-1)。DIフレームワークでは、このクラスを直接実行するのではなく、依存性を注入するための入れ替え用のクラスとします。play()メソッドでは、簡単なメッセージを表示させることにします。このとき、リスト2-2やリスト2-3のような別の具象クラスも用意しておくと、注入する依存性を入れ替えるしくみが理解しやすいでしょう。いずれも1)で作成したインターフェースを実装しています。

なお、クラスをstaticで宣言しているのは、インスタンスを生成していなくてもDIの設定をできるようにするためです。

リスト2-1 1)のインターフェースを実装した具象クラス

  // 設定時に参照できるようにstaticにしている
  static class PlayImpl implements Play  {
    // 実装されたメソッド
    public void play()  {
      System.out.println( "何かしてあそぶ!" );
    }

リスト2-2 別の具象クラスの例(1)

  static class Baseball implements Play  
    public void play()  {
      System.out.println( "野球してあそぶ!" );
    }
  }

リスト2-3 別の具象クラスの例(2)

  static class Soccer implements Play  {
    public void play()  {
      System.out.println( "サッカーしてあそぶ!" );
    }
  }

3) 1)と2)とを対応させる(DIの設定をする)モジュールを作成する

1)で作成したインターフェースと、2)で作成した具象クラスとを対応させるクラスを作成します。これにより、Playインターフェースを持つインスタンスが要求された場合に、2)で作成した具象クラス(依存性)のインスタンスが注入されるようにします。

Guiceでは、ここで作成するクラスをモジュールと呼び、com.google.inject.AbstractModuleクラスを継承して作成します。実際の処理はconfigure()メソッドに記述します。

リスト3 インターフェースと具象クラスとを対応させるモジュール

  class PlayModule extends AbstractModule  {
    protected void configure()  
      // PlayImplクラスを対応させる(この場合はSingletonとする)
      bind( Play.class ).to( PlayImpl.class ).in( Scopes.SINGLETON );
      // 別の具象クラスを対応させる場合(例)
      // bind( Play.class ).to( Baseball.class ).in( Scopes.SINGLETON );
      // bind( Play.class ).to( Soccer.class ).in( Scopes.SINGLETON );
    }
  }

4) 2)を実行するためのクラスを作成する

DIフレームワークでは、2)のインスタンスをを直接生成させるのではなく、2)を内包するクラスを別に作成しておき、そのインスタンスを生成します。そうすることで、注入される具象クラス(依存性)が入れ替わっても、処理上は同じクラスのインスタンスを生成することになるので、これを利用するほかのクラスには、具象クラスを入れ替えたことによる影響が及びません。

2)を実行するクラスをリスト4のように作成しました。Guiceでは、依存性を注入する箇所にアノテーション@Injectを付記します。ここではコンストラクタに付記されていますので、コンストラクタにより具象クラス(依存性)が注入されるコンストラクタ・インジェクションが行われることになります。

具体的には、このクラスのコンストラクタの引数でPlayインターフェースが指定されているため、Guiceによってインスタンスが生成される際には、3)により対応付けられた2)の具象クラスが注入されます。

ちなみに、@Injectがメソッドに付記されれば、メソッドを通じて依存性が注入されるメソッド・インジェクションが行われます。メソッド名がsetXXXXXの場合をとくにセッター(setter)・インジェクションと呼びます。

リスト4 Guiceによりインスタンスを生成されるクラス

  class Player implements Play  {
    private final Play play;
    // コンストラクタにより具象クラス(依存性)を注入される
    // (コンストラクタ・インジェクション)

ここまでで、基本的なDIの設定が完了しました。それではGuiceを使ってインスタンスを生成してみましょう。

インスタンスの生成と実行

Guiceによる依存性が注入されたインスタンスの生成は、先ほどのDIの設定から続けて以下の手順で行います。

  • 5) 3)の処理を行うクラスのインスタンスを生成する
  • 6) 4)のインスタンスを生成する
  • 7) 6)を実行する

5) 3)の処理を行うクラスのインスタンスを生成する

3)でインターフェースと具象クラスとを対応付けるモジュールを作成しましたが、Guiceではこの処理を実際に行うインスタンスとしてInjectorを生成します。具体的には、以下を実行します。

Injector injector = Guice.createInjector( new PlayModule() );

createInjector()メソッドの引数に、3)で作成したクラスのインスタンスを指定します。そうすると、このInjectorを用いた依存性の注入時には3)の処理が行われることになります。

6) 4)のインスタンスを生成する

5)で作成したInjectorにより4)のインスタンスを生成します。このとき3)の設定に基づいて、4)には2)が注入されます。インスタンスの生成時に1)や2)のクラス名などが一切記述されないことが、DIフレームワークを扱うメリットです。これによって、注入される具象クラス(依存性)がどんなものであろうと、すべて同じクラスのインスタンスとして扱うことができるのです。

Play play = injector.getInstance( Player.class );

7) 6)のインスタンスを実行する

6)で生成したインスタンスのメソッドを実行します。

play.play();

これで「何かしてあそぶ!」というメッセージが表示されるはずです。リスト3を変更すれば、リスト2-2やリスト2-3を実行させることができます。依存性の入れ替えが簡単にできることがおわかりいただけるでしょう。

GuiceによるDIの設定からインスタンスの生成と実行までの一連の流れが理解できたところで、続く後編では設定の応用について解説します。

後編は4/17(火)に公開予定。

おすすめ記事

記事・ニュース一覧