目次

DIとは?

Dependency Injection: 直訳すると依存性の注入
依存性…? → オブジェクトのこと
つまり、処理に必要な(使用する)オブジェクトを外部から注入できるようにするデザインパターン
オブジェクトを使う側は、オブジェクト自身を使用するのではなくオブジェクトを抽象化したインタフェースを注入することで抽象依存にすることができる(依存性を排除する)

メリット

  • インタフェースを通して実装オブジェクトを注入するため、インタフェースを使うロジック側は実装オブジェクトを意識せずに利用できる
  • 抽象依存であれば、別の実装(クラス)に差し替えることができ、クラス間を疎結合にできる(結合度を下げる)
  • Mockに置き換えることができるため、単体テストがしやすい

ex)

  • DIパターンでないコード
class Client {
  private Service service;

  public Client() {
    // Clientクラス内でServiceオブジェクトを生成
    this.service = new Service();
  }

  public void doSomething() {
    this.service.doSomething();
  }

  
}

class Service {
  public void doSomething() {
    
  }
  
}
  • DIパターンを使ったコード
class Client {
  private IService service;

  public Client(IService service) {
    this.service = service;
  }

  public void doSomething() {
    this.service.doSomething();
  }

  
}

interface IService {
  void doSomething();
}

class Service implements IService {
  public void doSomething() {
    
  }
}

依存オブジェクトを注入する方法

コンストラクタインジェクション (Constructor Injection)

推奨方法

  • コンストラクタの引数に対して、依存するコンポーネントを注入できる
  • 依存性を注入される(使う)クラスをDIコンテナにインスタンス化してもらうと、コンテナ管理されている依存性のインスタンスがコンストラクタの引数として呼び出される
  • フィールドインジェクションと比較して、インジェクションする数が増えると記述量は増え冗長になるが、それだけ多くのものに依存している状態がおかしい (単一責任の原則)
  • インスタンス化する際に必要な依存関係を強制することができる (インスタンス生成時点で状態が安定する)
  • フィールドをfinalとして宣言することができるため、イミュータブルなオブジェクトにしたり、必要な依存関係だけ不変にすることができる
  • 必須の依存関係の場合コンストラクタインジェクションを使用し、オプションに依存関係の場合セッターインジェクションを使用することで、その依存関係が必須かオプション化明確に区別できる
  • (JavaのSpringの話だが)@Autowiredの記述を省略できる

セッターインジェクション (Setter Injection)

  • セッターの引数に対して、依存するコンポーネントを注入できる

フィールドインジェクション (Field Injection)

  • 依存性を注入したいフィールドにアノテーション(JavaのSpringフレームワークだと@Autowired)をつけると、インスタンス生成時にそのフィールドのインスタンスがインジェクションされる
  • 記述量は少なくて済むが、依存性を注入される(使う)クラスのインスタンスをDIコンテナ外で使う場合、対象の依存性(フィールド)をインスタンス化できない(リフレクションを除いて)
  • → DIコンテナ以外では再利用できない (DIコンテナに依存してしまい、コンテナを使わないと適切にインスタンス生成できなくなってしまう)

自動的なDI (DIコンテナ)

DIコンテナ(アプリケーションにDI機能を提供するフレームワーク)を使用することで、依存性の注入をコード上に直接記述せずに、自動的に行うことができる

DIコンテナの例

参考記事