プログラムの依存性と依存性注入

# プログラムの依存性とは
プログラムの依存性とは、ある要素（モジュール、ライブラリ、関数など）が、他の要素の機能やデータに依存していることを指します。
依存している要素が変更されると、依存している側のプログラムにも影響が出る可能性があります。

# 依存性の問題点
プログラムの依存性は、以下のような問題を引き起こします。

- **変更の難しさ**
依存している要素を変更する場合、依存している側のプログラムにも変更が必要となるため、変更が難しくなります。
- **テストの難しさ**
依存している要素をモックやスタブで置き換えてテストすることが難しくなります。
- **再利用性の低さ**
依存している要素が異なる環境で利用できない場合、プログラムを再利用することができなくなります。

これらの依存性の問題を解決するために、**依存性の注入**という手法が使用されます。


# 依存性の注入とは
**依存性の注入**(Dependency Injection）とは、オブジェクトの依存関係を外部から注入し、コードの柔軟性と再利用性を高めるための設計パターンのことを指します。
言葉で説明されてもよくわからないと思うので、実際にコードを書いてみます。

## 通常の依存関係
```diff_javascript
// UserRepository.js
class UserRepository {
  getUser(userId) {
    // データベースからユーザー情報を取得する
    return { id: userId, name: 'John Doe' };
  }
}

// UserService.js
class UserService {
  constructor() {
    this.userRepository = new UserRepository();
  }

  getUserName(userId) {
    const user = this.userRepository.getUser(userId);
    return user.name;
  }
}

// 使用例
const userService = new UserService();
console.log(userService.getUserName(1)); // "John Doe"
```
従来の依存関係では、``UserService``クラスは``UserRepository``クラスのインスタンスを直接生成しています。これは、``UserService``クラスが``UserRepository``クラスの具体的な実装に依存していることを意味します。

この場合、``UserService``クラスを使用するには、``UserRepository``クラスが利用可能である必要があります。また、``UserRepository``クラスの具体的な実装が変更されると、``UserService``クラスのコードにも変更が必要になる可能性があります。

## 依存性注入を使った例
```diff_javascript
// UserRepository.js
class UserRepository {
  getUser(userId) {
    // データベースからユーザー情報を取得する
    return { id: userId, name: 'John Doe' };
  }
}

// UserService.js
class UserService {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  getUserName(userId) {
    const user = this.userRepository.getUser(userId);
    return user.name;
  }
}

// 使用例
const userRepository = new UserRepository();
const userService = new UserService(userRepository);
console.log(userService.getUserName(1)); // "John Doe"
```
一方、依存性注入では、``UserService``クラスは``UserRepository``クラスのインスタンスを外部から注入されます。これは、``UserService``クラスが``UserRepository``クラスの具体的な実装に依存せず、任意の``UserRepository``実装を受け取ることができることを意味します。

``UserService``クラスのコンストラクタに``UserRepository``クラスのインスタンスを渡すことで、``UserService``クラスは``UserRepository``クラスの具体的な実装に依存せず、どのような``UserRepository``実装でも動作することができます。

さらに、依存性注入を利用することで、モック（ダミー）オブジェクトを使用してテストが簡単に行えます。

## 依存性注入を利用しない場合のテストコード
```diff_javascript
const { UserService } = require('./UserService');

// テスト例
describe('UserService', () => {
  it('should return the user name', () => {
    const userService = new UserService();

    const userName = userService.getUserName(1);
    expect(userName).toBe('John Doe'); // 実際のデータベースアクセスが行われる
  });
});
```
テスト対象である``UserService``クラスだけでなく、依存している``UserRepository``クラスなども考慮する必要があります。実際のデータベースアクセスや外部システムとの連携などの処理がテスト結果に影響するため、テストコードが複雑になりやすく、メンテナンスも困難になります。

## 依存性注入を用いたテストコード
```diff_javascript
// UserService.test.js
const { UserService } = require('./UserService');

class MockUserRepository {
  getUser(userId) {
    // テスト用のダミーデータを返す
    return { id: userId, name: 'Mock User' };
  }
}

// テスト例
describe('UserService', () => {
  it('should return the user name', () => {
    const mockUserRepository = new MockUserRepository();
    const userService = new UserService(mockUserRepository);

    const userName = userService.getUserName(1);
    expect(userName).toBe('Mock User');
  });
});
```
テスト対象である``UserService``クラスのみを独立してテストすることができます。テスト対象以外のクラス（``UserRepository``クラスなど）はモックやスタブと呼ばれる偽のオブジェクトで置き換えるため、実際のデータベースアクセスや外部システムとの連携などを考慮する必要がありません。

# 依存性注入の利点
- **テストの容易さ**
テストを行う際に、実際の``UserRepository``クラスではなく、モックやスタブと呼ばれる偽のオブジェクトを注入することで、``UserService``クラス単体でテストしやすくなります。
- **変更の容易さ**
``UserRepository``クラスを変更する場合、``UserService``クラスのコードを変更する必要がありません。``UserService``クラスは、``UserRepository``クラスのインターフェースだけに依存しているため、``UserRepository``クラスの具体的な実装が変更されても、``UserService``クラスは動作し続けます。
- **再利用性の高さ**
``UserService``クラスは、``UserRepository``クラス以外のデータベースやファイルなどからユーザー情報を読み取る``UserRepository``クラスを注入することで、再利用することができます。

# まとめ
最後までご覧いただき、ありがとうございました。

依存性注入（DI）は、プログラムの依存性が引き起こす変更やテストの困難さ、再利用性の低さを解決するための重要な設計パターンです。DIを用いることで、クラスの依存を外部から注入し、コードの柔軟性と再利用性を高め、テストのしやすさや変更の容易さも向上します。

依存性を考慮し、適切な設計パターンを選択していくことで、より効果的なコードを実現していきましょう。

ご意見やご指摘がございましたら、ぜひお聞かせください。