依存性の注入

依存性注入(Dependency Injection:DI)とは



依存性注入(DI)は、あるオブジェクトが依存する他のオブジェクトを外部から受け取るデザインパターンです。英語の頭文字からDIと略されます。DIは、制御の反転(Inversion of Control: IoC)の一種であり、オブジェクトの生成と利用に関する関心を分離し、疎結合なプログラムを実現することを目的としています。

本来、dependencyを「依存性」と訳すのは厳密には正確ではないため、「依存オブジェクト注入」という用語を用いる文献もあります。

概要



DIを利用したプログラムでは、コンポーネント間の関係をインターフェースを用いて記述し、具体的なコンポーネントを直接指定しません。どのコンポーネントを利用するかは、DIコンテナや外部ファイルなどを用いて決定します。これにより、コンポーネント間の依存関係を緩くし、以下のようなメリットが得られます。

結合度の低下によるコンポーネント化の促進: コンポーネントが他のコンポーネントに強く依存しないため、再利用や変更が容易になります。
単体テストの効率化: 依存関係をモックオブジェクトなどに差し替えることで、特定のコンポーネントに依存しないテストが容易になります。
特定のフレームワークへの依存度低下: 特定のフレームワークに依存せず、コンポーネントを独立して開発できます。

DIという用語は、ソフトウェア開発者のマーティン・ファウラー氏によって作られました。それ以前から存在した制御の反転 (IoC) という概念を整理・限定することでDIが生まれました。現在では、DIコンテナとして知られるSpring Frameworkも、誕生当初はIoCという表現を使用していました。DIは、2000年代前半のJava開発において、Java EE(現・Jakarta EE)のEJBに対する批判を背景に広く採用されました。その後、DIの概念は標準仕様にも取り込まれ、Java EE 5では限定的な機能を持つEJB 3.0、Java EE 6ではより汎用的なDIコンテナとしてのCDIが定義されました。

DIの種類



プログラムに依存性を注入する方法には、主に以下の3つの手法があります。

インターフェース注入: 注入用のインターフェースを定義し、それを通して依存性を注入します。
セッター注入: セッターメソッドを定義し、それを通して依存性を注入します。
コンストラクタ注入: コンストラクタを通して依存性を注入します。

実装例



DIの実例として、Javaを使った株式売買システムを例に、DIを用いない場合、手動DI、そしてDIコンテナを用いた自動DIの例を以下に示します。

DIを用いない場合


java
public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {
private IStockAnalysisService stockAnalysisService;
private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl() {
this.stockAnalysisService = new StockAnalysisServiceImpl();
this.brokerageService = new OnlineBrokerageServiceImpl();
}
...
}


この例では、`VerySimpleStockTraderImpl`クラスが、`IStockAnalysisService`と`IOnlineBrokerageService`の実装クラスのインスタンスを直接作成しており、これらの実装に強く依存しています。

手動DI


java
public class MyApplication {
public static void main(String[] args) {
IStockAnalysisService stockAnalysisService = new StockAnalysisServiceImpl();
IOnlineBrokerageService brokerageService = new OnlineBrokerageServiceImpl();
IAutomatedStockTrader trader = new VerySimpleStockTraderImpl(stockAnalysisService, brokerageService);
}
}

public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {
private IStockAnalysisService stockAnalysisService;
private IOnlineBrokerageService brokerageService;

public VerySimpleStockTraderImpl(IStockAnalysisService stockAnalysisService, IOnlineBrokerageService brokerageService) {
this.stockAnalysisService = stockAnalysisService;
this.brokerageService = brokerageService;
}
...
}


この例では、`MyApplication.main()`が依存性の注入を行い、`VerySimpleStockTraderImpl`は特定の実装に依存しなくなっています。この例では、コンストラクタ注入が用いられています。

自動DI (DIコンテナ)


java
DIコンテナ設定ファイル例 (XML) >









//DIコンテナを使用してインスタンスを取得するイメージ
IAutomatedStockTrader trader = container.getBean("automatedStockTrader", IAutomatedStockTrader.class);


DIコンテナを使用することで、依存性の注入をコードに直接記述せず、設定ファイルに基づいて自動的に行うことができます。DIコンテナは、設定ファイルに基づき、インターフェースが要求された場合に、その実装クラスのインスタンスを生成し、依存関係を解決します。

DIコンテナには様々な種類が存在し、それぞれ異なる手法が用いられています。

DIを用いた単体テスト



DIを活用することで、単体テストの際に依存性をモックオブジェクトなどのテスト用クラスに簡単に差し替えることができます。

java
// テスト用クラス
class MockOnlineBrokerageService implements IOnlineBrokerageService { ... }
class MockStockAnalysisService implements IStockAnalysisService { ... }

// テストケース
public class VerySimpleStockTraderImplTest {
@Test
public void testPerformTrade() {
IOnlineBrokerageService brokerageService = new MockOnlineBrokerageService();
IStockAnalysisService stockAnalysisService = new MockStockAnalysisService();
IAutomatedStockTrader trader = new VerySimpleStockTraderImpl(stockAnalysisService, brokerageService);
...
}
}


上記例では、インターフェース`IOnlineBrokerageService`, `IStockAnalysisService`を実装したテスト用クラスをDIにより注入することで、実際のクラスを使用せずに単体テストを実現しています。これにより、DBやネットワークアクセスなど、単体テストが難しい場合でも、依存関係のみをテスト用のものに差し替えることで、テスト対象のプログラムを変更せずに単体テストを行うことが可能です。

HTMLにおけるDI



マークアップ言語であるHTMLでも依存性注入が行われます。WebComponentsの登場により、HTMLファイルを小さなコンポーネントの集合として記述することが可能になりました。しかし、親コンポーネントが子コンポーネントに依存してしまう問題を解決するために、`slot`要素を用いた依存性注入が用いられます。

html

差し込みコンテンツ1
差し込みコンテンツ2



上記の例では、``コンポーネントが2つの`slot`を持っています。利用時に`slot`属性を指定した要素を挿入することで、``コンポーネントは挿入された要素に直接依存せずに要素を利用できます。ただし、HTMLではプログラミング言語のような明示的なインターフェースがないため、型による安全性は保証されていません。しかし、適切に設計することで依存性を分離することは可能です。

DIコンテナ



DIの機能を提供するフレームワークはDIコンテナと呼ばれます。主なDIコンテナには、以下のようなものがあります。

Java
Jakarta EE (EJB, CDI)
Spring Framework
Seasar2
Google Guice

.NET
Entity Framework
Spring.NET
Microsoft.Extensions.DependencyInjection

PHP
Zend Framework 2
Symfony 2

関連項目



制御の反転 (IoC)
設定より規約
テスト自動化

もう一度検索

【記事の利用について】

タイトルと記事文章は、記事のあるページにリンクを張っていただければ、無料で利用できます。
※画像は、利用できませんのでご注意ください。

【リンクついて】

リンクフリーです。