依存性の注入

コンポーネント間の依存関係を排除するために、外部の設定ファイルなどでオブジェクトを注入できるようにするソフトウェアパターン

: Dependency injectionDIDI

dependency[1] [2][3]

概要

編集

DI

[4]







Dependency injection (IoC) DIDISpring FrameworkDIIoCDI2000JavaJava EEJakarta EEEJB[4] 2007Java EE 5EJB 3.02009Java EE 6DICDI[5]

DIの種類

編集

プログラムに依存性を注入する方法としては、以下のような手法が存在する。

インタフェース注入
注入用のインタフェースを定義して注入を行う方法
setter 注入
setter メソッドを定義して注入を行う方法
コンストラクタ注入
コンストラクタを定義して注入を行う方法

DIJavaDIDIDIDI


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() {
        . // omitted
    }
}

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

編集

DIXMLXMLDI使
    <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() {
         // omitted
    }
}

public class MyApplication {
    public static void main(String[] args) {
        IAutomatedStockTrader stockTrader =
            (IAutomatedStockTrader) DependencyManager.create(IAutomatedStockTrader.class);
        stockTrader.executeTrades();
    }
}

IAutomatedStockTrader使DIDIVerySimpleStockTraderImplVerySimpleStockTraderImplIStockAnalysisServiceIOnlineBrokerageService

DIDI

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を用いて依存関係のみをテスト用のものに差し替えることで、本来のテスト対象のプログラムには手を加えることなく、簡単に単体テストを行うことができる。[4]


HTML (HyperText Markup Language) 

WebComponents + Template+ ShadowDOMHTMLHTML Big  Small Big  Small slotslotinterfaceslot

<my-element-with-slot>2slotslotspan<my-element-with-slot>spanspaninterfaceinterfaceslot
    <!--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>

DIコンテナ

編集

DIの機能を提供するフレームワークはDIコンテナと呼ばれる[4]。 主なDIコンテナとしては、下記のようなものが存在する。

Java

.NET

PHP

注釈・出典

編集


(一)^ Dependency Definition & Meaning - Merriam-Webster. Merriam-Webster. 202293 : something that is dependent on something else especially : a territorial unit under the jurisdiction of a nation but not formally annexed by it

(二)^ Seasar2DIAOP Java200689 

(三)^  ~Ruby 201692 

(四)^ abcdJavaDependency InjectionDI. ITPro (2005218). 2014220

(五)^ Java EE 6: Understanding Contexts and Dependency Injection (CDI), Part 1.  (2010525). 2014220

(六)^ Microsoft.Extensions.DependencyInjection Namespace | Microsoft Docs

関連項目

編集