😸

依存性注入(DI)とは?: コードの再利用性、テスト容易性、修正柔軟性

2024/03/08に公開

はじめに

Udemyの下記コースを受講していたらDIを実装する箇所があり、気になったので深堀してみた

リバーシで学ぶアプリケーション設計入門〜仕様の整理からTypeScriptでの実装まで〜

DI (Dependency Injection)

  • 依存性の注入

    • クラスが他のクラスに依存している場合、その依存関係を外部から注入する方法
    • AクラスがBクラスに依存している場合、DIを使うとAクラスの外部からBクラスのインスタンスを提供できる
  • メリット

    • クラスの再利用性
      • DIを使うと、オブジェクトを同じクラスで再利用できる
    • テストのしやすさ
      • DIによりテストダブル(フェイク、モック、ダミーなど)を差し込んだテストが容易になる。特定のプロパティを持つクラスのふるまいを確認しやすくなる
    • コードの修正容易性
      • クラスの責任が明確になり、コードの修正時のコンフリクトが少なくなる

実装例(PHP)

  • まずはDIを使わない例
// Serviceクラス
class Service {
	private $repository;

	public function __construct() {
		$this->repository = new Repository();
	}

	public function updateUserData($data) {
		// 何かしらの処理
		$this->repository->update($data);
	}
}

// Repositoryクラス
class Repository {
	public function update($data) {
		// DB操作
	}
}

上記実装ではいくつかの問題点がある

  1. Serviceクラス単体でのテストができない
  2. Repositoryクラスに不具合があった場合、Serviceクラスが機能していても失敗する
  3. データベースの種類を変更する際に手間がかかる
  • DIを使った例
    • 主に「抽象に依存させる」ことが重要
    • 以下のコードは抽象クラスIRepositoryを介してFirebaseRepositoryクラスを注入する方法を示している
// IRepositoryインターフェース
interface IRepository {
	public function update($data);
}

// FirebaseRepositoryクラス
class FirebaseRepository implements IRepository {
	public function update($data) {
		// FirebaseでのDB操作
	}
}

// Serviceクラス
class Service {
	private $repository;

	// インターフェースを満たすクラスをコンストラクタで受け取る
	public function __construct(IRepository $repository) {
		$this->repository = $repository;
	}
	public function updateUserData($data) {
        // 何かしらの処理
        // 例: ユーザーデータのバリデーション
        // ...

        // データベース更新処理を呼び出す
        $this->repository->update($data);

        // 何かしらの後処理
        // 例: ログ出力
        // ...
    }
}

// DIを使った例
$firebaseRepository = new FirebaseRepository();
$handleUserData = new Service($firebaseRepository);
// ユーザーデータを更新
$userData = ['id' => 123, 'name' => 'Alice', 'email' => 'alice@example.com'];
$handleUserData->updateUserData($userData);

このDIを使った実装では、以下のメリットがある

  • Serviceクラス単体でのテストが可能
  • Repositoryクラスの変更が最小限に抑えられる

DIを活用することで、依存先クラスを変更するだけで済むため、柔軟性が向上する

まとめ

DIをかつようすることでクラスの再利用性とコードの修正容易性、テストのしやすさなど保守性に優れた手法かと思うが、設計次第ではあえて実装しないのも手段かと思ってしまった
例えば単体テストを想定しない、インターフェースも実装しないのであればあえてDIを活用するメリットも薄れてしまってかえって手間が増えてしまいそう
アプリケーションの設計に応じて適切に使用したいところ

Discussion