第23回 Guiceその1
今回のテーマは「Guice」です。
まず「Guiceとは何ぞや」というところから始めます。 Guice(ジュースと読みます)はGoogleがオープンソースとして公開したDIフレームワークです。 SpringやSeasar2といった有名どころとの大きな違いは、依存性注入の設定をxmlファイルに記述するのではなく、Javaで記述するという点です。
Javaで依存性注入の設定をするということに、どういうメリットがあるのでしょうか。 実はxmlによる設定の場合、クラス名などの記述ミスがあっても、プログラムを実行してみないと間違いがわからないのです。 Javaでの設定の場合は、コンパイル時点でエラーが発生するので、実行前に記述ミスが明らかになります。 また、JUnitなどのテスティングフレームワークによる単体テストも容易になります。
それでは、簡単なプログラムを使って、GuiceによるDIを説明していきます。
public class Player {
public void play() {
System.out.println("ウォームアップします。");
Badminton badminton = new Badminton();
badminton.play();
System.out.println("クールダウンします。");
}
}
public class Badminton {
public void play() {
System.out.println("バドミントンして遊びます。");
}
}
BadmintonクラスがないとPlayerクラスはコンパイルさえできません。 依存関係の解決前の状態です。
では、Guiceを使用してコードを変更していきます。 まずSportsインターフェースを作成し、Badmintonクラスを変更します。
public interface Sports {
void play();
}
public class Badminton implements Sports {
@Override
public void play() {
System.out.println("バドミントンして遊びます。");
}
}
次にSportsインターフェースとBadmintonクラスを結びつけるためのSportsModuleクラスを作成します。
GuiceではModuleインターフェースを実装することで依存性の注入の設定を行うのですが、直接Moduleインターフェースを実装するのではなく、AbstractModuleを継承することで、若干記述を減らすことができます。
configureメソッドでSportsインターフェースとBadmintonクラスを結びつけるための具体的な処理を行っています。
なお、bind(クラスA).to(クラスB)の場合において、クラスAとクラスBがis-a関係にないとコンパイルエラーが発生します。
これはジェネリクスの型変数によって実現されている処理です。
public class SportsModule extends AbstractModule {
@Override
protected void configure() {
bind(Sports.class).to(Badminton.class);
}
}
PlayerクラスにはSports型のフィールドを持たせて、Injectアノテーションを記述します。
これにより、Sports型のフィールドに依存性が注入されます。
Injectアノテーションはフィールドに限らず、コンストラクタや任意のメソッドに指定できます。
また、アクセス修飾子がprivateであっても関係ありません。
import com.google.inject.Inject;
public class Player {
@Inject
private Sports sports;
public void play() {
System.out.println("ウォームアップします。");
sports.play();
System.out.println("クールダウンします。");
}
}
最後にPlayerクラスを実行するクラスを作成します。
ここで先ほどのSportsModuleクラスを使用して、注入を実行するInjectorを作成します。
作成されたInjectorによって、Playerクラスのインスタンスが依存性注入された状態で生成されます。
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new SportsModule());
Player player = injector.getInstance(Player.class);
player.play();
}
}
以上の構成により、PlayerクラスとBadmintonクラスの依存関係が解消されました。
ちなみに、new演算子でPlayerクラスのインスタンスを生成した場合、playメソッドを実行したら確実にNullPointerExceptionが発生します。 プログラマにnew演算子を使わせないためには、コンストラクタをprivateにしましょう。
Guiceはコンストラクタがprivateなクラスでもインスタンス生成できますので、PlayerクラスとBadmintonクラスのコンストラクタを
privateにした場合でも、上記のコードは問題なく動作します。
次週も引き続きGuiceによるDIを学びます。

