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

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

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

Guice2.0 beta 紹介記事の翻訳(5)

原文:IBM Developer : Sorry, that page no longer exists
訳1:Guice2.0 beta 紹介記事の翻訳(1) - 山本大の日記
訳2:Guice2.0 beta 紹介記事の翻訳(2) - 山本大の日記
訳3:Guice2.0 beta 紹介記事の翻訳(3) - 山本大の日記
訳4:Guice2.0 beta 紹介記事の翻訳(4) - 山本大の日記

プロバイダーメソッド(Provider methods)

Other uses for provider methods
If you're nervous about the annotations that come with the Guice territory, or if you can't use them (you're creating a third-party class, for instance), provider methods solve your problem elegantly. Because your provider method lives in your module, you can annotate it without worrying that the rest of your source will see the annotations. For example, Listing 16 contains a provider method for BadgerBoy (a third-party hero):


Guiceの領域に縛られてしまうことからGuiceアノテーションを使うことには気が引けるという人や、サードパーティー製のクラスをインスタンス化しているために対象クラスにアノテーションが使えないという人のために、プロバイダーメソッドを使うという方法があります。プロバイダーメソッドは、これらの問題をエレガントに解決します。
なぜなら、プロバイダーメソッドはモジュールの中で生存するからです。これにより、ソースが置かれている場所からアノテーションが参照できるかどうかを気にすることなくアノテーションを利用することが出来ます。次のリスト16の例では、アナグマボーイ(BadgerBoy)というサードパーティー製のヒーローのためにプロバイダメソッドを使っています。


リスト16. プロバイダメソッドを使えば、ソースにアノテートせずアノテーションが使える。(A provider method allows you not to annotate the source)

@Provides @Inject
private BadgerBoy provideBadgerBoy(WeaselCopter copter) {
return new BadgerBoy(copter);
}

With this provider method, you can use Guice to configure BadgerBoy, and even to select which Vehicle he needs, all without changing BadgerBoy at all. This means that you don't need to use @Inject or binding annotations such as @Fast if you don't want to. You can use provider methods to select your dependencies instead.
Which technique you choose depends on your personal preference, whether you're using third-party classes, and how tightly you want to tie a class to its dependencies. Provider methods allow the totally hands-off approach. Annotated dependencies allow configuration information to live a little closer to the class, which makes the source more comprehensible while preserving flexibility and testability. Your application will likely use both ways to specify dependencies as the circumstance warrants.
You're sick of sending Frog Man on every adventure. You'd like a random hero for each new escapade. However, Guice's default binder API doesn't allow a call like "bind the Hero class to a different implementation each call." However, you can tell Guice to use a special method to create every new Hero. Listing 15 shows a new method added to the HeroModule, annotated with the special @Provides annotation:


このプロバイダーメソッドを使えば、このアナグマボーイ(BadgerBoy)を構成するためにGuiceを使うことが出来ます。
そして彼が必要とする乗り物(Vehicle)を選ぶこともできます。しかも、アナグマボーイについては、全く変更する必要がありません。
つまり、気に入らなければ@Injectや@Fastアノテーションのようなバインディングアノテーションを使わなくても良いということです。プロバイダーメソッドは、代わりとなる依存性を選択するために使えます。
どのテクニックを使うかは、個人的な趣味や、サードパーティークラスを使っているかどうかや、クラスと依存との結合度をどれぐらい強くしたいかによって選んでください。
プロバイダーメソッドは、総合的にハンズオフアプローチを提供します。
アノテーション付けする方法では、依存性を設定情報をクラスという小さく閉じた部分に置くことができ、柔軟性やテストの容易さを保持したままソースをより理解しやすくしてくれます。
アプリケーションでは、状況によって依存性を特定するために両方の方法を利用するでしょう。
たとえば、あらゆる冒険にフロッグマンを送り込むのが我慢できないとします。ランダムなヒーローをそれぞれの新たに脱出する度に適用したいとします。しかしながら、Guiceの標準のバインダーAPIでは、"それぞれの呼び出しでの異なる実装のためにヒーロークラスをバインドする"というように呼び出すことは許されていません。とはいえ、Guiceに対して全ての新しいヒーローを作るために、次のような特別なメソッドを使うように指示することができます。
リスト15は、HeroModuleに、特殊な@Providesアノテーションで注釈した新しいメソッドを追加するところを表わしています。


リスト15. プロバイダをカスタム生成ロジックのために利用する(Using a provider to write custom creation logic)

@Provides
private Hero provideHero(FrogMan frogMan, WeaselGirl weaselGirl) {
  if (Math.random() > .5) {
    return frogMan;
  }
  return weaselGirl;
}

Guice automatically discovers all methods in your Modules that have the @Provides annotation. Based on the return type of Hero, it works out that when you ask for a hero, it should call this method to provide it. You can stuff provider methods with logic to construct your object, pick it randomly, look it up from a cache, or obtain it by other means. Provider methods are an excellent way to integrate other libraries into your Guice module. They're also new as of Guice 2.0. (The Guice 1.0 way was to write custom provider classes, which were clunkier and more verbose. If you've decided to use Guice 1.0, the user's guide has documentation on the old way, and the sample code supplied with this article has a custom provider you can look at.)
Guice automatically injects the provider method in Listing 15 with the correct arguments. This means Guice will find WeaselGirl and FrogMan from its list of bindings without you needing to construct them manually within the provider method. This illustrates the "it's turtles all the way down" principle. You rely on Guice to provide your dependencies, even when you're configuring your Guice module itself.
Asking for a Provider instead of a dependency
Suppose you want multiple heroes in one story ― a Saga. If you ask Guice to inject a Hero, you'll get only one. But if you ask for a "provider of heroes," you can create as many as you like, as in Listing 17:


Guiceはモジュールの中から自動的に@Providesアノテーションを持つメソッドを発見します。
ヒーローへの問い合わせがあったときに、ヒーローの戻り値の型に基づいて分析します。
そうして、プロバイドするためにこのメソッドが呼び出されます。
オブジェクトを構築するためのロジックと一緒に、プロバイドメソッドをstuffできます。ランダムに選び出すというロジックです。キャッシュからそれを探し出すか、ほかの理由でそれを取り出します。プロバイダメソッドは、ほかのライブラリをGuiceモジュールに統合するエクセレントな方法だといえます。
それらはまた、Guice2.0の新機能でもあります(Guice1.0の方法は、よりclunkier でverboseなカスタムプロバイダクラスを書くことでした。もし、Guice1.0を使ることを決めるなら、ユーザーガイドにその古い方法が記載されています。そして、この記事で提供するサンプルコードは、見つけることができる、カスタムプロバイダーを持っています。)
Guiceは、自動的に正しい引数でリスト15のプロバイダメソッドをインジェクトします。これはプロバイダメソッドを使って手動で構築する必要をなくし、Guiceバインディングのリストからイタチガール(WeaselGirl )とフロッグマン(FrogMan)を見つけることを意味しています。
これは"地球は巨大な亀が支えてる"という原則を例示しています。
Guiceが依存性を提供することに依存します。Guiceモジュール自体を構成するときでさえ。
依存性の代わりにプロバイダーを呼び出すことは、
複数のヒーローを1つのストーリーに登場させることができます。これぞ英雄伝ですね。もしヒーローをインジェクトするようにGuiceに問い合わせたら、唯一つをだけを取得するでしょう。しかし、もし"ヒーロープロバイダ"を問い合わせたら、いくらでも好きなだけ生成することができます。リスト17でそれをやってみます。



リスト17. インスタンス化を制御するプロバイダーをインジェクトする(Inject a provider to take control of instantiation)

public class Saga {
  private final Provider<Hero> heroProvider;

  @Inject
  public Saga(Provider<Hero> heroProvider) {
    this.heroProvider = heroProvider;
  }

  public void start() throws IOException {
    for (int i = 0; i < 3; i++) {
      Hero hero = heroProvider.get();
      hero.fightCrime();
    }
  }
}

Providers also let you delay the retrieval of the heroes until the saga actually starts. This is handy if the hero depends on data that's time- or context-sensitive.
The Provider interface has one method: get. To access the object provided, you simply call the method. Whether or not you get a new object each time, and how that object is configured, depends on how Guice was configured. (See the next section on Scopes for details about singletons and other long-lived objects.) In this case, Guice uses your @Provides method because it's the registered way to construct a new Hero. This means the saga should consist of a mix of three random heroes.
Providers shouldn't be confused with provider methods. (In Guice 1.0, they were considerably harder to tell apart.) Although the Saga gets its heroes from your custom @Provides method, you can ask for a Provider of any Guice-instantiated dependency. If you wanted, you could rewrite FrogMan's constructor according to Listing 18:


プロバイダーは、英雄伝が実際に始まるまでヒーローの取得を遅らせることもできます。これは、ヒーローが時間やコンテキストに敏感なデータに依存しているような場合に便利です。プロバイダーインターフェイスは「get」という1つのメソッドを持っています。プロバイドされたオブジェクトにアクセスするためには、単にこのメソッドを呼び出すだけです。Guiceが構成するどの方法によってオブジェクトを設定するにせよ、新しいオブジェクトがいつでも手に入ります。(次のセクションでの、シングルトンに関する詳細なスコープの話と、そのほか長期間生存するオブジェクトについてを参照ください。)
このケースではGuiceは@Providesメソッドを使います。なぜならそれは新しいヒーローを構成するための登録された方法だからです。これは、3人のランダムなヒーローのミックスからなる英雄伝であることを意味しいます。
プロバイダーは、プロバイダーメソッドを使って混乱させるでしょう。(Guice1.0では、分離して説明するのは非常に困難でした。)とはいえ、英雄伝はカスタムの@Providesメソッドによってヒーローを取得できます。
すべてのGuiceインスタンス化した依存性のProviderを問い合わせることが出来ます。もし望むなら、FrogManコンストラクターを書き換えることもできます。それを説明したのがリスト18です。


リスト18. 依存性の代わりにプロバイダーに問い合わせることができる。(You can ask for a Provider instead of the dependency)

@Inject
public FrogMan(Provider<Vehicle> vehicleProvider) {
  this.vehicle = vehicleProvider.get();
}

(Note that you didn't have to change the module code at all.) This rewrite doesn't serve any purpose; it just illustrates that you can always ask for a Provider instead of the dependency directly.


(モジュールのコードはまったく変えていないことに注目してください。) この書き換えはまったく目的はありません。
依存性を直接呼び出すことの代わりに、いつでもProviderを呼び出せることを示すだけの例示です。