Javaにおける依存性注入パターン:疎結合による保守性の向上
約3分
別名
- 制御の反転(IoC)
- 依存性反転
依存性注入デザインパターンの目的
オブジェクトの依存関係の作成をその使用から分離し、より柔軟でテスト可能なコードを可能にする。
実世界の例を用いた依存性注入パターンの詳細な説明
現実世界の例
高級レストランで、シェフが料理を準備するためにさまざまな食材を必要とする場面を想像してください。シェフが各食材のために個別に異なるサプライヤーに行く代わりに、信頼できるサプライヤーが毎日必要なすべての新鮮な食材を配達します。これにより、シェフは食材の調達を心配することなく、調理に集中できます。
依存性注入デザインパターンでは、信頼できるサプライヤーが「インジェクター」として機能し、シェフ(オブジェクト)に必要な依存関係(食材)を提供します。シェフは、これらの依存関係がどこから来たかを知らなくても使用でき、依存関係の作成と使用の間に明確な分離を確保します。この設定は、ソフトウェアシステムと同様に、キッチンでの効率、柔軟性、および保守性を向上させます。
平易な言葉で
依存性注入は、クライアントの依存関係の作成をクライアント自身の動作から分離します。
Wikipediaによると
ソフトウェアエンジニアリングでは、依存性注入とは、オブジェクトが依存する他のオブジェクトを受け取る手法です。これらの他のオブジェクトは依存関係と呼ばれます。
Javaにおける依存性注入パターンのプログラム例
年老いた魔法使いは、時々パイプにタバコを詰めて吸うのが好きです。しかし、彼は単一のタバコブランドに依存したくなく、すべてのブランドを相互に楽しむことができるようにしたいと思っています。
まず、Tobacco
インターフェースと具体的なブランドを紹介します。
@Slf4j
public abstract class Tobacco {
public void smoke(Wizard wizard) {
LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(),
this.getClass().getSimpleName());
}
}
public class SecondBreakfastTobacco extends Tobacco {
}
public class RivendellTobacco extends Tobacco {
}
public class OldTobyTobacco extends Tobacco {
}
次に、Wizard
クラス階層を示します。
public interface Wizard {
void smoke();
}
public class AdvancedWizard implements Wizard {
private final Tobacco tobacco;
public AdvancedWizard(Tobacco tobacco) {
this.tobacco = tobacco;
}
@Override
public void smoke() {
tobacco.smoke(this);
}
}
最後に、年老いた魔法使いに任意のブランドのタバコを与えるのがいかに簡単かを示します。
public static void main(String[] args) {
var simpleWizard = new SimpleWizard();
simpleWizard.smoke();
var advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco());
advancedWizard.smoke();
var advancedSorceress = new AdvancedSorceress();
advancedSorceress.setTobacco(new SecondBreakfastTobacco());
advancedSorceress.smoke();
var injector = Guice.createInjector(new TobaccoModule());
var guiceWizard = injector.getInstance(GuiceWizard.class);
guiceWizard.smoke();
}
プログラムの出力
11:54:05.205 [main] INFO com.iluwatar.dependency.injection.Tobacco -- SimpleWizard smoking OldTobyTobacco
11:54:05.207 [main] INFO com.iluwatar.dependency.injection.Tobacco -- AdvancedWizard smoking SecondBreakfastTobacco
11:54:05.207 [main] INFO com.iluwatar.dependency.injection.Tobacco -- AdvancedSorceress smoking SecondBreakfastTobacco
11:54:05.308 [main] INFO com.iluwatar.dependency.injection.Tobacco -- GuiceWizard smoking RivendellTobacco
実世界の例を用いた依存性注入パターンの詳細な説明

Javaで依存性注入パターンを使用する場合
- クラス間の結合を減らし、アプリケーションのモジュール性を高めることを目指す場合。
- オブジェクトの作成プロセスが複雑である場合、またはクラスの使用から分離する必要がある場合。
- 依存関係をモックまたはスタブ化できるようにすることで、より簡単なユニットテストが必要なアプリケーション。
- SpringやJakarta EE(以前のJava EE)など、オブジェクトのライフサイクルと依存関係を管理するフレームワークまたはライブラリ内。
Javaにおける依存性注入パターンの現実世界での応用
- Spring、Jakarta EE、Google Guiceなどのフレームワークは、コンポーネントのライフサイクルと依存関係を管理するために依存性注入(DI)を広範囲に使用します。
- 容易に交換可能なコンポーネントを備えた柔軟なアーキテクチャを必要とするデスクトップおよびWebアプリケーション。
依存性注入パターンの利点とトレードオフ
利点
- モジュール性と関心の分離を強化します。
- 依存関係の簡単なモックを可能にすることで、ユニットテストを簡素化します。
- 疎結合を促進することにより、柔軟性と保守性を向上させます。
トレードオフ
- 特に大規模なプロジェクトでは、構成に複雑さをもたらす可能性があります。
- 依存性注入パターンやフレームワークに慣れていない開発者にとっては、学習曲線が長くなる可能性があります。
- オブジェクトのライフサイクルとスコープを慎重に管理する必要があります。
関連するJavaデザインパターン
- ファクトリーメソッドおよび抽象ファクトリー:DIメカニズムが注入するインスタンスを作成するために使用されます。
- サービスロケーター:サービスまたはコンポーネントを特定するためのDIの代替手段ですが、DIほど効果的にルックアッププロセスを分離しません。
- シングルトン:アプリケーション全体でサービスの単一インスタンスを提供するために、DIと組み合わせてよく使用されます。