依存性の注入

依存性の注入（いそんせいのちゅうにゅう、英: Dependency injection）とは、コンポーネント間の依存関係をプログラムのソースコードから排除するために、外部の設定ファイルなどでオブジェクトを注入できるようにするソフトウェアパターンである。英語の頭文字からDIと略される。

例

DIの例として、以下にJavaによるDIを用いない場合と手動でのDI、ならびにDIコンテナをイメージした自動でのDIのサンプルコードを示す。
初めに、一連のサンプルで用いる各コンポーネントのインタフェースを示す。
public interface IOnlineBrokerageService {
    String[] getStockSymbols();
    double getBidPrice(String stockSymbol);
    double getAskPrice(String stockSymbol);
    void putBuyOrder(String stockSymbol, int shares, double buyPrice);
    void putSellOrder(String stockSymbol, int shares, double sellPrice);}public interface IStockAnalysisService {
    double getEstimatedValue(String stockSymbol);}public interface IAutomatedStockTrader {
    void executeTrades();}

DIを用いない状態

以下はDIを用いない場合の実装例である。
public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

    private IStockAnalysisService analysisService = new StockAnalysisServiceImpl();
    private IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

    public void executeTrades() {
        ….
    }}public class MyApplication {
    public static void main(String[] args) {
        IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl();
        stockTrader.executeTrades();
    }}
VerySimpleStockTraderImplクラスでは、直接IStockAnalysisService, IOnlineBrokerageServiceインタフェースを実装したクラスのインスタンスを作成しており、これらの実装に深く依存してしまっている。

手動でのDI

上記のコードを、手動でDIを行うようにリファクタリングすると下記のようになる。
public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {

    private IStockAnalysisService analysisService;
    private IOnlineBrokerageService brokerageService;

    public VerySimpleStockTraderImpl(
            IStockAnalysisService analysisService,
            IOnlineBrokerageService brokerageService) {
        this.analysisService = analysisService;
        this.brokerageService = brokerageService;
    }
    public void executeTrades() {
        …
    }}public class MyApplication {
    public static void main(String[] args) {
        IStockAnalysisService analysisService = new StockAnalysisServiceImpl();
        IOnlineBrokerageService brokerageService = new NewYorkStockExchangeBrokerageServiceImpl();

        IAutomatedStockTrader stockTrader = new VerySimpleStockTraderImpl(
            analysisService,
            brokerageService);
        stockTrader.executeTrades();
    }}
この例では、MyApplication.main()が依存性の注入を行っており、VerySimpleStockTraderImpl自体は特定の実装に依存しなくなっている。なお、この実装ではコンストラクタ注入の手法が用いられている。

自動的なDI

DIコンテナを用いることで、依存性の注入をコード上に直接記述せず、自動的に行うことが可能である。こうした手法を用いる場合、依存性は外部のXMLファイルやメタデータにて定義する。上記のコードを、XMLを用いるDIコンテナを使用するようリファクタリングした例が下記である。
    <contract id="IAutomatedStockTrader">
        <implementation>VerySimpleStockTraderImpl</implementation>
    </contract>
    <contract id="IStockAnalysisService" singleton="true">
        <implementation>StockAnalysisServiceImpl</implementation>
    </contract>
    <contract id="IOnlineBrokerageService" singleton="true">
        <implementation>NewYorkStockExchangeBrokerageServiceImpl</implementation>
    </contract>
public class VerySimpleStockTraderImpl implements IAutomatedStockTrader {
    private IStockAnalysisService analysisService;
    private IOnlineBrokerageService brokerageService;

    public VerySimpleStockTraderImpl(
            IStockAnalysisService analysisService,
            IOnlineBrokerageService brokerageService) {
        this.analysisService = analysisService;
        this.brokerageService = brokerageService;
    }
    public void executeTrades() {
        …
    }}public class MyApplication {
    public static void main(String[] args) {
        IAutomatedStockTrader stockTrader =
            (IAutomatedStockTrader) DependencyManager.create(IAutomatedStockTrader.class);
        stockTrader.executeTrades();
    }}
この例では、IAutomatedStockTraderのどの実装を使用するかの判断はDIコンテナに委ねられている。インタフェースが要求されたDIコンテナは、設定ファイルに基づきその実装であるVerySimpleStockTraderImplクラスのインスタンスを返す。さらに、VerySimpleStockTraderImplのIStockAnalysisServiceとIOnlineBrokerageServiceの依存性に対して、同様にコンストラクタ注入を行う。
DIコンテナには数多くの種類があり、上で示した例はそのごく一部でしかない。実際にはDIコンテナごとに様々な手法が用いられている。

DIを用いた単体テスト

DIを用いることで、単体テストにおいて簡単に依存性をテスト用のクラス（モックオブジェクト等）に差し替えることができる。以下はDIを用いた、前述のVerySimpleStockTraderImplクラスのテストケースの例である。この例では、IOnlineBrokerageService,  IStockAnalysisServiceインタフェースを実装したテスト用クラスを作成し、DIによりそれを注入することで、実際のクラスを用いることなく、単体テストを実現している。
public class VerySimpleStockBrokerTest {
    // IOnlineBrokerageServiceを実装した単純なスタブ
    public class StubBrokerageService implements IOnlineBrokerageService {
        public String[] getStockSymbols() { 
            return new String[] {"ACME"};
        }
        public double getBidPrice(String stockSymbol) {
            return 100.0; // （テストに十分な値）
        }
        public double getAskPrice(String stockSymbol) { 
            return 100.25;
        }
        public void putBuyOrder(String stockSymbol, int shares, double buyPrice) {
             Assert.Fail("Should not buy ACME stock!");
        }
        public void putSellOrder(String stockSymbol, int shares, double sellPrice) {
             // このテストでは使用しない
             throw new NotImplementedException(); 
        }
    }

    public class StubAnalysisService implements IStockAnalysisService {
        public double getEstimatedValue(String stockSymbol) {
            if (stockSymbol.equals("ACME")) 
                return 1.0;
            return 100.0;
        }
    }

    public void TestVerySimpleStockTraderImpl() {
        // このテスト専用の依存性を指定するため、DIコンテナに直接登録している
        DependencyManager.register(
            IOnlineBrokerageService.class,
            StubBrokerageService.class);
        DependencyManager.register(
            IStockAnalysisService.class,
            StubAnalysisService.class);

        IAutomatedStockTrader stockTrader =
            (IAutomatedStockTrader) DependencyManager.create(IAutomatedStockTrader.class);
        stockTrader.executeTrades();
    }}
実装がDBやネットワークにアクセスする場合、また古いEJBのような重たいコンポーネントの場合、そのままでは単体テストを行うことは難しい。しかし、上記のようにDIを用いて依存関係のみをテスト用のものに差し替えることで、本来のテスト対象のプログラムには手を加えることなく、簡単に単体テストを行うことができる。

HTML

マークアップ言語であるHTML (HyperText Markup Language) でも依存性の注入がおこなわれる。
WebComponents（カスタム要素 + Template要素+ ShadowDOM）の登場により、巨大なHTMLファイルを小さなHTML要素コンポーネントの集合として記述することが可能になった。しかし大きなコンポーネント Big が小さなコンポーネント Small を包む形でコーディングすると、Big が Small に依存してしまう。そこでslot要素を用いた依存性の注入がおこなわれる。slot要素は弱いinterfaceとして働き、slot要素を用いて定義されたカスタム要素を利用する際に依存性をタグで囲むことで注入できる。

下記の例では大きなコンポーネント<my-element-with-slot>が2つの受け入れ可能slotを持っている。利用時にslotを指定したspan要素を挿入することで、<my-element-with-slot>はspan要素に直接依存せずにspan要素を利用できる。プログラミング言語のような明示的interfaceがない（interfaceによる型指定slot要素がない）ために型支援を受けた安全な依存性の注入は現時点ではおこなえないが、適切に設計することで依存性を切り分けることは可能である。
    <!--when define-->
    <script>
      class myElementWithSlot extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: "open" });
          shadowRoot.innerHTML = `            <h2>My Element</h2>            <h3>inserted #1: <slot name="slot1">no contents</slot></h3>            <h4>inserted #2: <slot name="slot2">no contents</slot></h4>          `;
        }
      }
      customElements.define("my-element-with-slot", myElementWithSlot);
    </script>

    <!--when use-->
    <my-element-with-slot>
      <span slot="slot1">dependency-one</span>
      <span slot="slot2">dependency-two</span>
    </my-element-with-slot>