Dependency Injection(依存性の注入)は、ずっと受け入れられるようになってきた。そして近年、SOA,TDDそして多くの他の要因が、人気を増してきていることを含んで、多くの要因に後押しされて、Dependency Injectionは、利用しやすい手法になってきた。こうしたことに伴って、Dependency Injectionフレームワークの使用が増えてきており、そのハイライトが最近、 Java EE 6にも使われたことである。例を使って、Bob Martin氏は、自分のアプリケーションコードとお好みのDependency Injectionフレームワークとを、疎結合化する手法の適用を勧めている。
彼の Dependency Injection Inversion に関する投稿で、[Bobおじさん]こと、Martin氏は、メッセージを次の簡単な声明でズバリ言っている:
... 私は、[Dependency Injection]フレームワークのコードが、私のアプリケーションの至る所を汚して欲しくありません。私は、フレームワークをうまく疎結合のままにしたい、そして私のコードの本体にべったりして欲しくないんです。
この点を説明するために、Martin氏は、以下の依存性をそのコンストラクタで、定義しているBillingServiceクラスの生成を中心に展開する例をあげている:
public class BillingService { ... BillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } ... }
彼は、最初、BillingService クラスのインスタンスを生成するのに、 Guice (Googleの Dependency Injection フレームワーク)を使うコード片を紹介している:
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService billingService = injector.getInstance(BillingService.class); billingService.processCharge(2034, "Bob"); }
(Guiceを使って)ここで実際に起きていることの詳細のいくつかを検討した後に、Martin氏は、以下の事実を問題視している、すなわち、BillingServiceのインスタンスを生成するために、今やあなたは、Guiceにinjectorを明示的に頼まなければならない、立場にいる、という事実である。BillingServiceを必要としているコードは、もはやBillingServiceが依存しているものには、密に依存していない(いいこと)、しかし、今や、Guiceに密に依存している。
あなたは、単に1つのまずい事を別のものと交換しただけなのか?Martinは、そうだ、と言っている:
Dependency Injectionは、Dependency Inversion(依存性の逆転)の特別なケースに過ぎません。私は、Dependency Inversionは、非常に重要なので、私は、Guiceへの依存を逆転させたいと思います!私は、たくさんの具体的なGuiceへの依存が私のコード中の至る所に存在して欲しくありません。
後で、彼は、手製のFactoryのようなオブジェクトをどのように使って、アプリケーションのDIフレームワークへの依存性を制御し、そして減らせるかを示している:
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService.factory = new BillingServiceFactory(injector); } ... // 私のシステムの奥深い所 BillingService billingService = BillingService.factory.make(); billingService.processCharge(2034, "Bob");
なぜこの手法が役に立つかについて、詳細に説明しながら、Martin氏は、このように言っている:
今や、Guiceの全ては、よく理解できる1箇所にあるので、私は、この手法が好きです。私のアプリケーションの至る所には、Guiceは、いません。Guiceっぽいファクトリによって、Guiceが私のアプリケーションの至る所を汚染するのを防いでいます。更にいいのは、私が、Guiceを他のDIフレームワークと交換したいと思ったら、私は、どのクラスを、どのように変える必要があるのかを正確に知っています。そう、私は、Guiceと私のアプリケーションとが結合しないように、維持しています。
説明中で面白い点は、使われた全ての例で、BillingService自身は、Dependency Injectionの原則に固執していることである;BillingServiceが依存するもの(CreditCardProcessor と TransactionLog)は、どのような実装が使われているかについての知識が、BillingServiceの外に置かれている。この説明のために、この記事の終りに、TransactionLog と CreditCardProcessorから、簡単な手製の"テストダブル(代役)"を使う、BillingServiceのJUnitテストを示して、アプリケーションが実行時に依存性を注入するのに、どの方法を選ぼうが、このテストは、ちゃんと走る、と言っている。
(Martin氏のテストダブルやそれらを"手作りする"ことをはっきりと、選択することに関連した傍注として、Gary Bernhardt氏が、 大変面白い記事 を投稿し、Javaや他の静的に型づけする言語では、このような決定は非常に賢いかもしれないが、Pythonのような動的言語では、必ずしもそうでない、と強調している。)
さて、あなたは、Dependency Injectionを使うか?DIフレームワークを使うか? もしそうなら、あるいは、またもしそうでないとしても、これらのことを、どれだけあなたは、理解されただろうか?