Locked History Actions

guice/Manual/BestPractices/ModulesShouldBeFastAndSideEffectFree

モジュールは速く副作用の無いこと

コンフィギュレーションのための外部XMLではなく、Guiceでは通常のJavaコードを使ってモジュールを記述する。 Javaならわかりやすく、IDEで操作可能で、リファクタリングしてもおかしくならない。

しかし、Java言語のフルパワーを生かすにはコストがかかる。モジュール内で「やりすぎ」がたやすくできてしまう。 モジュール内でHTTPサーバを開始しデータベースに接続するというような誘惑にかられる。 こんなことをしないように! 重量級のモジュールでは問題が発生しかねない。

  • モジュールがスタートアップするが、シャットダウンしない。モジュール内でデータベース接続を開始するとそれをクローズする方法がない。
  • モジュールをテストしなければならなくなる。モジュール内でデータベース接続をオープンすると、それをテストするのは難しい。
  • モジュールはオーバライド可能である。Guiceモジュールはオーバライドをサポートしており、製品向けサービスを軽いテスト用のものに代替することが可能であるが、製品サービスをモジュール実行の一部として起動してしまうと、そのようなオーバライドが意味をなさなくなる。

モジュール自体の中でそのような仕事をさせるのではなく、特定の抽象の中でそのような仕事ができるようなインターフェースを定義すべきである。 例えばこのように。

public interface Service {
  /**
   * サービスをスタートする。このメソッドはサービスが完全に開始するまでブロックする。
   */
  void start() throws Exception;

  /**
   * サービスを停止する。完全にシャットダウンするまでブロックする。
   */
  void stop();
}

インジェクタを作成した後は、そのサービスを開始することでアプリのブートストラップを終了する。 アプリをストップさせるときは、リソースを完全に解放するためのシャットダウンフックも提供する。

  public static void main(String[] args) throws Exception {
    Injector injector = Guice.createInjector(
        new DatabaseModule(),
        new WebserverModule(),
        ...
    );

    Service databaseConnectionPool = injector.getInstance(
        Key.get(Service.class, DatabaseService.class));
    databaseConnectionPool.start();
    addShutdownHook(databaseConnectionPool);

    Service webserver = injector.getInstance(
        Key.get(Service.class, WebserverService.class));
    webserver.start();
    addShutdownHook(webserver);
  }