Google Guice(その7) 実装クラスの指定方法

インタフェースの実装クラスを指定する方法は、その2で書いたbind(インタフェース).to(実装クラス);が基本です。しかし他にもいくつかあって、その中でもちょっと毛色が違う@ImplementedByアノテーションを紹介します。
これはインタフェースにアノテーションで実装クラスを指定する方法です。

@ImplementedBy(ServiceImpl.class)
public interface Service {
	String getResponse(String msg);
}

bind()で実装クラスを指定したらそちらが優先、指定が無ければこのアノテーション指定が使われるので、デフォルトの実装をアノテーションで指定しておき、必要があった時だけbind()で上書きするという使い方ができます。
指定が無くてよいので、呼び出し側は↓だけ。

public class Main {
	public static void main(String[] args) {
		Injector injector = Guice.createInjector(new Module() {
			public void configure(Binder binder) {}
		});
		Client client = injector.getInstance(Client.class);
		client.execute();
	}
}

アノテーション書くだけというのはやはり楽です。

が、しかし、当然この方法はかなり議論が分かれるところで、TSSのスレでもかなり書かれています*1。一番大きな論点はたぶん「インタフェースがアノテーションと実装クラスに依存してしまう」点です*2。これは受け入れられない人には絶対受け入れられなさそうです。ちなみにコンフィギュレーションアノテーションをどう使うかについて、Springの父Rod Johnson氏もブログを書いていました(すいません、読んでません)。
私は自分が使う分には正直大きな拒否感は感じないです。単純に考えると、ライブラリを提供する際にインタフェースとデフォルトの実装をこれで紐付ければ、バインディングコードやXMLを一緒にくっつけなくてもデフォルトで動く、とか良いかもしれません。その場合ユーザが書いたバインディングは全てデフォルトの差し替えなので、変更や拡張部分が分かりやすい、みたいな。

ちなみにGuiceのproject ownerであるcrazybobさんはこの@ImplementedByについて、TSSで以下のように書いてます(英語弱いので訳は適当です)。

私自身は@ImplementedByに大いに賛成という訳ではないけど、以下のような事を考えています。

  • 本当に必要としているユーザもる。
  • 勿論使わなくても構わないんだから、不要な人は無視すれば良いいだけ。
  • (この機能は)間違った事を簡単にしているのではなく、良い事(インタフェースを使う事)を簡単にしている。
  • 我々はSpringではない。1つの最も良いやり方をサポートしたいのであって、可能なあらゆるやり方を対象にしているわけではないのだ。加えて、押し付けがましくなるつもりはなく、この機能は低リスクだと考えている*3

ところでデフォルトの実装の指定方法として、@ImprementedBy以外のやり方として、@Inject(optional=true)と書く方法もあります。

@Inject(optional=true)
private Service service = new ServiceImpl();
//...
bind(Service.class).to(AnotherServiceImpl.class);
// ↑この行を実行するとインジェクトが行われる。実行しない場合は元のまま


とりあえず元TSSウォッチャーとしては、Rickard Oberg氏が相変わらず元気に発言してるのを見てなんだか嬉しかったのでした。

*1:ところで1年前くらいに見た時、TSSは随分寂しくなったなと思いましたが、このスレは盛り上がってますね。

*2:他には、実装を切り替える場合にエレガントでないとか、設定が分散するとかですかね

*3:上記の通り、アーキテクチャや思想を破壊するようなものではないから提供しても大きな問題は無い、という意味だと思われます