Guice2.0のベータに関する記事があったので、またぼちぼち訳してみます。
Dependency injection with Guice
Level: 中級 Nicholas Lesiecki (ndlesiecki@apache.org), Software engineer, Google
2008/11/09
Guice is Google's open source dependency injection framework for Java〓 development. It enables better testing and modularity by taking away the pain of writing your own factories. Nicholas Lesiecki offers a tour of the most important Guice concepts that will leave you ready to Guice up your applications.
Guiceはグーグルが提供する、Java開発のためのオープンソースDIフレームワークです。
このフレームワークは、プログラマーを独自のファクトリーを作る手間から開放し、より良いテストとモジューラビリティーを可能にします。
この記事はGuiceを使うための準備として、最も重要なGuiceのコンセプトのツアーを提供します。
Guiceはdependency injection (DI)フレームワークです。私はここ数年、開発者にDIを使うことを推奨してきました。なぜなら、DIを使うことでメンテナンス性、テスト容易性、柔軟性が好転するからです。新しい技術を採用するために、プログラマを説得する一番良い方法は、それを実際に簡単に使ってもらうことです。Guiceに対するエンジニアの反応を見ていると、GuiceはDIを本当に簡単なものにしてくれます。そして、結果としてその運営はGoogoleを離れました。私は、流れのまま続くことを希望しています。この記事でGuiceを学ぶこととても簡単にすることで、それを実現したいと思います。
Guice 2.0 beta
As I write this, the Guice team is working hard on Guice 2.0 and expects to release before the end of 2008. An early beta is posted on the Google Code download site (see Resources). This is good news, because the Guice team has added features that will make your Guice code easier to use and understand. The beta lacks some features that will make it into the final version, but it's stable and high quality. In fact, Google uses the beta version in production software. I advise you to do the same. I've written this article specifically for Guice 2.0, covering some new Guice features and glossing over features from 1.0 that have been deprecated. The Guice team has assured me that the features I cover won't change between the current beta and final release.
私がこの記事を書いているとき、GuiceチームはGuice2.0を2008年中にリリースするために一生懸命働いています。
早期Bata版が、Google Code ダウンロードサイト上にアップされました。(参照)
これは良いニュースです。なぜなら、Guiceチームは簡単に使えて学習も容易な新しい機能を作りこむからです。
Beta版で欠けているいくつかの機能は、最終バージョンでは組み込まれるでしょう。しかし、Beta版も安定していて高い品質です。
事実、Google自体がBetaバージョンをソフトウェア製品で使っています。私としては、同じようにあなたの製品でも使ってみてはとアドバイスしてあげたいぐらいです。
私は、この記事でGuice2.0の特徴を書きます。いくつかのGuice2.0の機能と、1.0で不評だった点をブラッシュアップした機能をカバーします。
Guiceチームは、私が書いた機能については、現在のベータ版から最終リリースの間まで変えないと私に確証してくれました。
もし、読者の方がDIについて既に知っているなら、なぜフレームワークがあなたの助けになるかは知っていることでしょうから、「Guiceの基本的なインジェクション」のセクションまで飛ばしてもかまいません。そうでなければ、DIの利益について学んでいきましょう。
DIを使う状況The case for DI
では、サンプルから始めることにしましょう。例として、スーパーヒーローアプリケーションを作っていきます。
Frog Manという名前のヒーローを実装していきます。"リスト1"にコードと最初のテストコードも一緒に記述します。
(単体テストの価値についての解説は、もう良いですよね?)
リスト1. ヒーロー と 彼に関するテスト
public class FrogMan { private FrogMobile vehicle = new FrogMobile(); public FrogMan() {} // crime fighting logic goes here... } public class FrogManTest extends TestCase { public void testFrogManFightsCrime() { FrogMan hero = new FrogMan(); hero.fightCrime(); //make some assertions... } }
リスト2. 面倒な依存性
java.lang.RuntimeException: Refinery startup failure. at HeavyWaterRefinery.<init>(HeavyWaterRefinery.java:6) at FrogMobile.<init>(FrogMobile.java:5) at FrogMan.<init>(FrogMan.java:8) at FrogManTest.testFrogManFightsCrime(FrogManTest.java:10)
FrogMobileが、HeavyWaterRefineryを生成していて、それで、私の作ったテストコードの仲ではそれらを生成する方法が無いのです。
プログラムの中でそうすることはできますが、テストにおいては別の手を使うことを許してくれません。
実生活では、重水を精製(HeavyWaterRefinery)するようなことはありませんが、しかし、リモートサーバーや肥満気味のデータベースに依存しているようなことはよくあります。
原則は同じです。依存性は、起動するのが難しいそして、相互に遅くなる、さらにたいていテストを失敗させるということです。
DI入門 Enter DI
この問題を回避するには、インターフェイスを作ります。(例えば、Vehicle(乗り物)というような)。そして、FrogManクラスのコンストラクタ引数にVehicleを受け取るようにします。
リスト3でそれを記載します。
リスト3. インターフェイスへの依存、そしてインジェクト
public class FrogMan { private Vehicle vehicle; public FrogMan(Vehicle vehicle) { this.vehicle = vehicle; } // crime fighting logic goes here... }
この書き方は、DIの核心部分です。つまり、クラスが、その依存性を生成する代わりにインターフェイスを介して参照しすることです。(または、静的参照を使います。)
リスト4は、DIがどのようにテストを簡単にするかを表しています。
Listing 4. テストで面倒な依存性の代わりにモックを使う方法
static class MockVehicle implements Vehicle { boolean didZoom; public String zoom() { this.didZoom = true; return "Mock Vehicle Zoomed."; } } public void testFrogManFightsCrime() { MockVehicle mockVehicle = new MockVehicle(); FrogMan hero = new FrogMan(mockVehicle); hero.fightCrime(); assertTrue(mockVehicle.didZoom); // other assertions }
このテストは、FrogMobileの代わりに手書きのモックオブジェクトを使っています。
DIはコストをかけて精製所を立ち上げるというテストの苦痛から解放してくれるだけではなく、テストがFrogMobileについて知っている状態をキープすることができます。これに必要なのは、Vehicleインターフェイスだけです。加えて、テストの作成を簡単にしてくれます。
DIはコードのモジュール性とメンテナンス性を、総合的に援助します。さて、もしもFrogBargeからFrogMobileに切り替えたいと思ったなら、FrogManクラスを変更することなく行なうことができます。FrogManが依存するのはインターフェイスだけです。
ここまでは理解できたと思います。しかしながら、私がDIに初めて触れたときにはこう考えました。
「とてもいいけど、FrogManを呼び出す側はFrogMobileについて知らないといけないんじゃない?(それに精製所や精製所の依存性についてなどなど、、)」
しかし、それが本当ならDIをまだ理解していないといえます。強制的に、呼び出し側の負担を装う代わりに、オブジェクトの生成や依存性を管理するファクトリーを書くようにすれば良いのです。
ファクトリーは、フレームワークで提供されています。ファクトリーは沢山の退屈な繰り返しのコードを必要とします。
その最も良い場合であっても、プログラム実装者や解読者を悩ませます。さらに最悪のケースでは、その面倒さから、ファクトリーをまったく記述しないことになります。GuiceやそのほかのDIフレームワークは、オブジェクトの生成を設定する柔軟な「スーパーファクトリー」を提供するものです。
フレームワークによる設定は、自分でファクトリーを書くよりも全然簡単です。結果として、プログラマーはDIスタイルでより多くのコードを書くことになります。より多くのテストをして、より良いコードを書いて、プログラマーが幸せになることにつながるのです。