【デザインパターン】DI

# DIとは
DI(Dependency Injection)とは、オブジェクトを外部から注入することでコンポーネント間の依存関係を薄くするデザインパターンのこと。

## 例
### DIしない場合
```Manager.cs
class Manager
{
    private readonly Employee employee = new Employee();

    public List<Employee> GetStaff()
    {
        var employeeList = employee.GetEmployees();

        //マネージャーが確認したい社員のみにする
        //(ここでは仮にリストの1～5番目の社員とする)
        var staff = employeeList.GetRange(1, 5);
        return staff;
    }
}
```
```Employee.cs
class Employee
{
    public int No;

    public List<Employee> GetEmployees()
    {
        var employeeList = new List<Employee>();

        //DBから従業員を10人取得したと仮定する
        for (int i = 0; i < 10; i++)
        {
            employeeList.Add(new Employee() { No = i });
        }

        return employeeList;
    }
}
```
``` Program.cs
class Program
{
    static void Main(string[] args)
    {
        Employee employee = new Employee();

        var manager = new Manager(employee);

        foreach (var staff in manager.GetStaff())
        {
            Console.Write(staff.No);
            //結果 12345
        }
    }
}
```
`Manager`クラスは`Employee`クラスをクラス内部でnewしているため、`Manager`クラスは`Employee`クラスに依存しています。

その結果、以下のような問題が発生します。

- 依存クラスの変更が必要
`Employee`クラスを別のクラス(従業員全体に関するメソッドだけ切り出して`EmployeeUtil`クラスにするなど)で実装することになった場合、`Manager`クラスにも変更が必要になってしまいます。 
  
  さらに`Employee`クラスに依存するクラスが`Manager`クラス以外にもある場合、アプリケーションの各所に変更の影響が発生してしまいます。

- 単体テストができない
テスト用のテーブルが作成前やテスト用のDBが無い場合、`GetEmployees()`を使った従業員のリストが取得できません。  
  
  そのため、このメソッド関連の単体テストができなくなってしまいます。

### DIする場合
```IEmployee.cs
interface IEmployee
{
    public List<Employee> GetEmployees();
}
```
```Manager.cs
class Manager
{
    private readonly IEmployee employee;

    public Manager(IEmployee employee)
    {
        this.employee = employee;
    }

    public List<Employee> GetStaff()
    {
        var employeeList = employee.GetEmployees();

        //Managerが確認したい社員のみにする
        //(ここでは仮にリストの1～5番目の社員とする)
        var staff = employeeList.GetRange(1,5);
        return staff;
    }
}
```
```Employee.cs
class Employee : IEmployee
{
    public int No;

    public List<Employee> GetEmployees()
    {
        var employeeList = new List<Employee>();

        //DBから従業員を10人取得したと仮定する
        for (int i = 0; i < 10; i++)
        {
            employeeList.Add(new Employee() { No = i });
        }

        return employeeList;
    }
}
```
``` Program.cs
class Program
{
    static void Main(string[] args)
    {
        IEmployee employee = new Employee();

        var manager = new Manager(employee);

        foreach (var staff in manager.GetStaff())
        {
            Console.Write(staff.No);
            //結果 12345
        }
    }
}
```
`IEmployee`インターフェースを作成し、`Manager`クラスの外部からインターフェースを介して`Employee`クラスを呼び出すようにしました。
  
その結果、以下のメリットがあります。

- 依存クラスの変更が不要
オブジェクトを外部から設定することで依存クラスの変更が不要になります。  
  
  例えば、`Employee`クラスを別クラスに置き換えたとしても、`Manager`クラスへの変更は不要になります。
``` EmployeeUtil.cs
class EmployeeUtil : IEmployee
{
    public List<Employee> GetEmployees() {
      //Employeeクラスと同じ処理のため省略
    }
}
```
``` Program.cs
class Program
{
    static void Main(string[] args)
    {
        //Employee→EmployeeUtilに変更
        IEmployee employee = new EmployeeUtil();

        var manager = new Manager(employee);

        foreach (var staff in manager.GetStaff())
        {
            Console.Write(staff.No);
            //結果 12345
        }
    }
}
```


- 単体テストしやすくなる
DBなど外部からデータを取得している場合で単体テストが難しいケースでもテスト用の結果を返すことができるため、単体テストがしやすくなります。
  
  例えば、`Manager`クラスの`GetStaff()`の中で呼ばれる`GetEmployees()`の結果を`MockEmployee`クラスで定義することでテスト用のDBが無い場合でも単体テストができるようになります。

``` MockEmployee.cs
public class MockEmployee : IEmployee
{
    public List<Employee> GetEmployees()
    {
        var employeeMock = new List<Employee>();

        for (int i = 0; i < 10; i++)
        {
            employeeMock.Add(new Employee() { No = i });
        }

        return employeeMock;
    }
}
```
``` ManagerTest.cs
public class ManagerTest
{
    [Test]
    public void GetStaffTest()
    {
        IEmployee employeeMock = new MockEmployee();
        var manager = new Manager(employeeMock);

        Assert.AreEqual(manager.GetStaff().Count, 5);

    }
}
```

# DIコンテナ
上記の例では手動でDIをしていましたが、手動でDIすると実装を変更したいときに修正箇所がアプリケーション各所になってしまいます。(`Employee`クラスでnewしていた箇所を全て`EmployeeUtil`クラスにしたい場合など)

そのような事態を避けるため、DIの依存関係を管理することができるDIコンテナを利用します。DIコンテナを利用することで、オブジェクト間の依存関係を事前に登録しておくことでき、DIする箇所が1箇所になるため保守性を向上させることができます。

C#では`Microsoft.Extensions.DependencyInjection`と呼ばれるDIコンテナ用のライブラリがあるため、以下ではそちらを利用してDIします。(今回は`Program`クラス内ですが、規模が大きいアプリケーションの場合は設定用のファイルを作成した方が依存関係を管理しやすくなります)
``` Program.cs
class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection();

        //事前に依存関係を登録
        services.AddSingleton<IEmployee>(new Employee());
        var provider = services.BuildServiceProvider();

        //サービスの型を取得(ここではEmployeeクラスを取得)
        var employee = provider.GetRequiredService<IEmployee>();
        var manager = new Manager(employee);

        foreach (var staff in manager.GetStaff())
        {
            Console.Write(staff.No);
            //結果 12345
        }

    }
}
```

# まとめ
DIとはオブジェクトを外部から注入することにより、コンポーネント間を疎結合にすることができるデザインパターン。
  
疎結合にすることで、アプリケーションを保守しやすく、また単体テストしやすくすることができる。

# 参考
- [.NET での依存関係の挿入](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/dependency-injection)
- [依存性の注入](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5#DI%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A)
- [DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる](https://qiita.com/okazuki/items/a0f2fb0a63ca88340ff6)
- [DIコンテナを使うメリットが分からない](https://ja.stackoverflow.com/questions/11750/di%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E4%BD%BF%E3%81%86%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%81%8C%E5%88%86%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84)