レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

Guice User's Guide ■2. Plain Old Factories

プレーン・オールド・ファクトリー

Before we discovered dependency injection, we mostly used the factory pattern. In addition to the service interface, you have a service factory which provides the service to clients as well as a way for tests to pass in a mock service. We'll make the service a singleton so we can keep this example as simple as possible.

Dependency Injectionが発明されるまえは、ファクトリーパターンがよく使われていました。サービスインターフェイスに加えて、クライアントにサービスを提供するサービスファクトリーを作ります。のみならず、テストのためにモックサービスに引き渡すためのサービスファクトリーも作ります。このサンプルでは出来るだけシンプルにするためにそのサービスをシングルトンにすることにしましょう。

public class ServiceFactory {

  private ServiceFactory() {}
    
  private static Service instance = new ServiceImpl();

  public static Service getInstance() {
    return instance;
  }
  
  public static void setInstance(Service service) {
    instance = service;
  }
} 

Our client goes directly to the factory every time it needs a service.

サービスが必要になるたびに、クライアントがファクトリーに直接参照しに行きます。

public class Client {

  public void go() {
    Service service = ServiceFactory.getInstance();
    service.go();
  }
}

The client is simple enough, but the unit test for the client has to pass in a mock service and then remember to clean up afterwards. This isn't such a big deal in our simple example, but as you add more clients and services, all this mocking and cleaning up creates friction for unit test writing. Also, if you forget to clean up after your test, other tests may succeed or fail when they shouldn't. Even worse, tests may fail depending on which order you run them in.

クライアントは、とてもシンプルです。しかしこのクライアントのためのユニットテストは、モックサービスでテストをパスした後でクリーンアップすることを覚えておかなくてはいけません。この作業はこの程度のシンプルなサンプルであれば大きな作業ではありませんが、しかしより多くのクライアントやサービスを追加して、すべてのモック作成とクリーンアップをするのは、ユニットテストをつくる事への抵抗感となります。また、テストの後のクリーンアップを忘れたら、他のテストの成否が思わしくない結果になるでしょう。さらに悪い事には、実行したプログラムの系列の依存するテストが失敗します。

public void testClient() {
  Service previous = ServiceFactory.getInstance();
  try {
    final MockService mock = new MockService();
    ServiceFactory.setInstance(mock);
    Client client = new Client();
    client.go();
    assertTrue(mock.isGone());
  }
  finally {
    ServiceFactory.setInstance(previous);
  }
}

Finally, note that the service factory's API ties us to a singleton approach. Even if getInstance() could return multiple instances, setInstance() ties our hands. Moving to a non-singleton implementation would mean switching to a more complex API.

最終的に(finally句で)、シングルトンアプローチへ結び付けるように、サービスファクトリーのAPIを使って記述しています。たとえば、getInstance()メソッドが、複数のインスタンスを返したとすると、setInstance()は自分の手で結び付けます。シングルトンにしないように実装するには、より複雑なAPIに切り替えなくてはなりません。