「依存性逆転」に"逆転"感を感じられない話【初学者向け】

※本記事は[網屋 Tech Blog Advent Calendar 2024](https://qiita.com/advent-calendar/2024/amiya)の12月18日分の記事です。

エンジニア1年目の自分が何も知らない状態から初めてインターフェースについて学んだ時、「インターフェースを切ることで依存性の方向が逆転する」にピンと来なかった話。

## 「依存性の逆転」の概説
ここでの「依存性」という言葉は、例えばクラスAがクラスBで定義している具象メソッドを呼び出しているような場合に「クラスAがクラスBに依存している」というように使う。わかりやすいようにクラスAをServiceA、クラスBをRepositoryBとする。
```cs:サンプルコード.cs
public class static RepositoryB
{
    private readonly Dictionary<int, string> _items = new Dictionary<int, string>();

    public void AddItem(string item)
    {
        int id = _items.Count + 1;
        _items.Add(id, item);
    }
}

public class ServiceA
{
    public void AddItemToRepository(string item)
    {
        RepositoryB.AddItem(item);
    }
}
```
##### クラス図
```mermaid
classDiagram
    ServiceA ..> RepositoryB
    class ServiceA{
      +AddItemToRepository(string item)void
    }
    class RepositoryB{
      -Dictionary<int, string> _items
      +AddItem(string item)void
    }
```
クラスBのインターフェースとしてIRepositoryBを実装すると、RepositoryBのシグネチャをIRepositoryBによって定めることができて、ServiceAはIRepositoryBの抽象メソッドを呼び出すだけでよくなる。

```cs:インターフェースを実装したサンプルコード.cs
public interface IRepositoryB
{
    void AddItem(string item);
}

public class RepositoryB : IRepositoryB
{
    private readonly Dictionary<int, string> _items = new Dictionary<int, string>();

    public void AddItem(string item)
    {
        int id = _items.Count + 1;
        _items.Add(id, item);
    }
}

public class ServiceA
{
    private readonly IRepositoryB _repositoryB;

    public ServiceA(IRepositoryB repositoryB)
    {
        _repositoryB = repositoryB;
    }

    public void AddItemToRepository(string item)
    {
        _repositoryB.AddItem(item);
    }
}
```

##### インターフェースを実装したクラス図
```mermaid
classDiagram
    ServiceA ..> IRepositoryB
    IRepositoryB <|.. RepositoryB
    class ServiceA{
      -IRepositoryB _repositoryB
      +ServiceA(IRepositoryB repositoryB)
      +AddItemToRepository(string item)void
    }
    class IRepositoryB{
      +AddItem(string item)void
    }
    class RepositoryB{
      -Dictionary<int, string> _items
      +AddItem(string item)void
    }
```

この時、クラスAからクラスBへの依存が解消され、クラスBはインターフェースへ依存するようになった。このことを「依存性の方向が逆転する」と一般的に表現する。

## ピンと来なかった理由
依存関係が"逆転"したというより、クラスAとクラスBの間にある直接的な依存関係が、インターフェースを挟むことで間接的な依存関係に変わったと表現する方がしっくりくる。両者の結合の度合が減少しただけで、決してクラスBがクラスAに依存するようになったわけではないので"逆転"感をそんなに感じない。
[wikipedia](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87)では『抽象レイヤーを加える事により、上位レベルレイヤーと下位レベルレイヤーの両方とも top から bottom に向かう従来の依存関係を減らす事ができるが、"反転" の概念は下位レベルレイヤーが上位レベルレイヤーに依存することを意味しない』と紹介されてある。一方で、依存性の逆転について解説している記事の多くで、インターフェースを切ることで依存性の方向が逆転するとしていて、「逆転」という言葉の意味を明確にして使っているわけではなく、ふわっとしているように思う。このことが初学者の理解を妨げているような気がする（少なくとも、自分はそうだった..）。

## どう解釈するか
クラスBの視点から見れば、クラスAの存在を気にすることなく、インターフェースにだけ注目していればよい。ここでインターフェースとクラスAをひとまとまりにしてみれば、クラスBはこのひとまとまりに依存しているように見える。

![dependency-inversion.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953214/15d86a29-9968-a4c4-fd66-9489210b73fa.png)


そのほかに、何かの記事にあったのが（何の記事か忘れてしまった..）、クラスAはクラスBで定義したシグネチャを使わせてもらう立場だったのが、インターフェースを切ることでクラスBはインターフェースから「シグネチャはこう使え」といわれる立場に変わり、これを立場の逆転と解釈できると書いてあった。このような解釈もわかりやすいと思う。

### 最後に
気になっていたことについて記事にしてまとめてみました。
この記事が初学者の学習の助けになれば幸いです。