WEB

【初学者向け】DI(依存性の注入)についての解説

WEB
この記事は約7分で読めます。

はじめに

株式会社インプルの奈良です。

プログラミングには、DI(依存性の注入)という概念が存在します。

この記事では、DIが何であるか、そしてなぜ重要なのかを初学者にも理解しやすいように解説します。

DI(依存性の注入)とは

DI(Dependency Injection)は、オブジェクト指向プログラミングにおいて、コードの柔軟性と再利用性を高めるための設計パターンです。

依存性とは

プログラミングにおける依存性とは、あるクラスが他のクラスのメソッドやデータを使用することを指します。

通常、あるクラスが別のクラスに依存している場合、その依存関係はコード内で直接作成されますが、DIを使用すると、これらの依存関係を外部から注入することができます。

なぜDIが重要なのか

従来のプログラミングでは、クラス内で直接依存しているオブジェクトを生成していました。

しかし、これだと以下の問題が生じてしまいます。

テストの困難さ

依存オブジェクトが固定されているため、テスト時に別のオブジェクトを使用するのが難しい。

コードの再利用性の低下

依存オブジェクトが変わると、クラス自体も変更する必要がある。

緊密な結合

一つの部品の変更が他の部品に波及しやすい。

DIのメリット

DIのメリットは、主に以下の点に集約されます。

テストの容易さ

DIを使用すると、テスト中にモックオブジェクトやスタブを簡単に注入できます。これにより、実際の依存関係を持つオブジェクトを使わずにテストを行うことが可能になり、単体テストの効率と品質が向上します。

低結合性

DIは、クラス間の結合度を低下させます。オブジェクトの依存関係を外部から注入することで、クラスは具体的な依存関係の詳細を知らずに済むため、それぞれのクラスがより独立して動作します。

再利用性と保守性の向上

低結合性により、クラスやコンポーネントは再利用しやすくなります。また、一部のコンポーネントを修正しても、その変更がシステムの他の部分に影響を与えにくくなるため、保守が容易になります。

プログラムの可読性と管理のしやすさ

DIを用いると、依存関係がより明確になり、コードが読みやすくなります。これにより、プログラムの流れを追いやすくなり、新たな開発者がプロジェクトに参加しやすくなります。

拡張性の向上

新しい機能やコンポーネントを追加する際に、既存のコードを大幅に変更する必要がなくなります。DIを利用することで、新しい依存関係を容易に組み込むことが可能になり、システムの拡張がよりスムーズに行えます。

設計原則との整合性

DIは、SOLIDのようなオブジェクト指向設計原則に準拠しています。特に「依存性逆転の原則(Dependency Inversion Principle)」に直接関連しており、より健全な設計を促進します。

DIのないコード例

Javaを使用した例を見てみましょう。ここでは、ServiceクラスがRepositoryクラスに依存しています。

class Repository {
    public String getData() {
        return "データ";
    }
}

class Service {
    private Repository repository = new Repository();

    public String processData() {
        return repository.getData();
    }
}

public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        System.out.println(service.processData());
    }
}

処理の順序

  1. Main クラスの main メソッドが実行されます。
  2. Service オブジェクトが作成されます。
  3. Service クラスのコンストラクタが Repository オブジェクトを作成し、repository フィールドに割り当てます。
  4. processData メソッドが呼び出され、内部で repository オブジェクトの getData メソッドを呼び出します。
  5. getData メソッドから返されたデータ(”データ”)が processData メソッドに戻り、さらに main メソッドに戻ります。
  6. 返されたデータが画面に表示されます。

このコードでは、Service クラスは Repository クラスに直接依存しており、DIは使用されていません。

DIを使用したコード例

次に、DIを使用して依存関係を外部から注入する方法を見てみましょう。

class Repository {
    public String getData() {
        return "データ";
    }
}

class Service {
    private Repository repository;

    // コンストラクタで依存関係を注入
    public Service(Repository repository) {
        this.repository = repository;
    }

    public String processData() {
        return repository.getData();
    }
}

public class Main {
    public static void main(String[] args) {
        Repository repository = new Repository();
        Service service = new Service(repository);
        System.out.println(service.processData());
    }
}

処理の順序

  1. Main クラスの main メソッドが実行されます。
  2. Repository オブジェクトが作成されます。
  3. Service オブジェクトが作成され、コンストラクタに Repository オブジェクトが渡されます。これにより、repository フィールドに依存オブジェクトが注入されます。
  4. processData メソッドが呼び出され、内部で repository オブジェクトの getData メソッドを呼び出します。
  5. getData メソッドから返されたデータが processData メソッドに戻り、さらに main メソッドに戻ります。
  6. 返されたデータが画面に表示されます。

このコードでは、DIを使って Repository オブジェクトを Service オブジェクトに注入しています。これにより、Service クラスは Repository クラスの具体的な実装に依存しなくなり、柔軟性とテストのしやすさが向上しています。

DIの実用例

DIの実用例として、よく使われるのがフレームワークやライブラリにおけるDIの利用です。

例えば、JavaのSpringフレームワークや、PythonのDjangoフレームワークでは、DIがコアの機能の一部として組み込まれています。

これにより、開発者はフレームワークが提供する便利なサービスを簡単に使用し、カスタマイズできます。

DIの応用

DIは、単に依存関係を外部から注入するだけでなく、プログラムの設計思想を変えるきっかけにもなります。

例えば、DIを利用することで、設計原則である「単一責任の原則」や「開放/閉鎖の原則」を実現しやすくなります。

これらの原則を適用することで、よりメンテナンスしやすく、拡張可能なプログラムを作成することができます。

参考記事

DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる - Qiita
追記2022/11/12 追記この記事読んで、DI 便利だなって思ったらこちらも併せて読んでみてください。クリーンアーキテクチャーの開設の中で依存性逆転の説明が出てきます。難しいかもしれませんが…
依存性の注入(DI)について解説してみる | Tech Media | W2株式会社
最近は主に製品のユニットテストの整備を行っているので、ユニットテストを書く上で重要な概念である依存性の注入(DI)について解説してみようと思います。
依存性注入とは - IT用語辞典
依存性注入【DI】とは、コンピュータプログラムのデザインパターンの一つで、オブジェクトなどの間に生じる依存関係をオブジェクト内のコードに直接記述せず、外部から何らかの形で与えるようにする手法。あるオブジェクトAがオブジェクトBを呼び出してその機能を利用する場合「AはBに依存している」という。