インターフェースの役割について、自分なりの解釈のまとめなおし

[CleanArchitecture](https://www.amazon.co.jp/dp/B07FSBHS2V/ref=cm_sw_r_tw_dp_U_x_96bVCbRW5DECP)憧れ系なんだけど、凄く重要な要素なのに、Rubyエンジニアだから実感がわかない「インターフェース」について、考え方の整理もかねて、自分なりの解釈を纏めなおしてみた。


# インターフェースの役割の復習

## 依存関係の整理による保守性の確保

**「変更の可能性が高いコードをインターフェースの向こう側に閉じ込めてしまう」事で、保守が容易になる。**


### 無インターフェース例
Aクラスは、Bクラスを使っている。
ある時、Bクラスの実装が大幅に変更された。（これをB'クラスとする）
この時、Aクラスのコードは、どうなるか？

もしかすると大幅な変更が必要かもしれないし、もしかすると何も変えずに済むかもしれない。
それを知るためには、AはBにどうメッセージを投げていて、Bはどうそれを受けていて、B'ではどう変更されているのか等、関連するクラスの全てを調べ直す必要がある。

つまりAは、Bの存在自体に、大きな影響を受けていると言える。

**この様に、AがBを前提に書かれている場合、「AはBに依存している」という。**

コードの再調査は、大変な作業だ。
クラスが2つなら良いが、大規模なシステムになると、依存関係は急激に複雑になる。
Bに依存しているクラスはAだけではなく、5個10個と出て来るかもしれないし（Anとする）、Anの変更も必要であれば、Anに依存しているクラスもまた洗い出し、全て再調査する必要がある。

とてもやっていられない。
なので、**この依存関係は、システムが巨大化していく段階のどこかで、あらかじめ断ち切っておく必要がある。**


**その為に使う武器の一つが「インターフェース」になる。**


### インターフェース例

AクラスとBクラスを「インターフェース」を用いた実装で考えると、例えば以下の様になる。

* Aクラスは、Iインターフェースを使用している。
* Iインターフェースの実態は、Iインターフェースの仕様通りに実装された、Bクラスである。

ある時、Bクラスの実装が大幅に変更された。（これをB'クラスとする）
この時、Aクラスのコードはどうなるか？

何も変えないで済む。
何故なら、BクラスがB'クラスになっても、Iインターフェース自体は何も変更されていないからだ。
B'クラスはIインターフェースの仕様を忠実に守りつづけなければ、Iインターフェースの実態となる事が出来ない。

AはIを使うため、Iに依存している。さらは、BもIの仕様を守るため、Iに依存している。
**処理自体ではAがBを使用しているのだが、「依存は、AもBも、Iにのみしている」**。

BではなくIに依存しているため、AはBの変更の影響を受けない。
影響を受けないから、BがB'になっても、AやAnの再調査は必要が無い。
当然、それらに依存している無数のクラスの再調査も必要が無い。

とても修正が簡単になる。
Bを修正する時は、Bの事だけを考えていれば良い。


### まとめ

* **「変更の可能性が高いコードをインターフェースの向こう側に閉じ込めてしまう」事で、コードが保守しやすくなる。**
  * **これを実現するために、依存をインターフェースに集める様に実装しておく**


## 依存関係の整理による拡張性の確保

**「拡張の可能性が高いコードをインターフェースの向こう側に置いておく」事で、保守が容易になる**


### 無インターフェース例

AクラスがBクラスを使用している。
ある時、新しくC機能が必要になった。
C機能はBクラス（この機能をB機能とする）と良く似た性質を持っており、B機能とは排他的に実行される。


この時、C機能はどの様に実装されるか？

どの様にでも書く事が出来る。

Aクラスに手続き的に追加されるかもしれないし、Cクラスを追加してAクラスから呼び出すのかもしれない。
もしくは、Bクラスの中でB機能とC機能の実行分岐が追加されるのかもしれない。

どういう形でも実現が可能だが、どの場合でも、それぞれのクラスに、大幅な修正や構成変更が必要になる。そして、依存関係は断ち切れていないので、その影響範囲は無限に広い。

これがC機能だけなら良いが、D機能、E機能と拡張機能が追加される様な事があると、修正箇所が爆発的に増え、手に負えなくなる。
**機能の拡張は、簡単に行える様にしておく必要がある。**


**そのための武器の一つとして、ここでも「インターフェース」を用いる事が出来る。**

### インターフェース例

AクラスとBクラスを「インターフェース」で実装すると考えると、例えば以下の様になる。

* Aクラスは、Iインターフェースを引数から取得している。
* Aクラスは、Iインターフェースの関数を実行している。
* Iインターフェースの実態は、Iインターフェースの仕様通りに実装された、Bクラスである。


ある時、新しくCという機能が必要になった。
C機能はBクラス（この機能をB機能とする）と良く似た性質を持っており、B機能とは排他的に実行される。

この時、C機能はどの様に実装されるか？

どの様にでも書く事が出来る。
が、簡単に解決する方法がある。

1. Cクラスを、Iインターフェースの仕様通りに実装する。
2. A機能に引数として渡すIインターフェースの実態を、BもしくはCクラスとする。

この方法であれば、AクラスもBクラスも、何も修正をする必要が無い

Bとはそもそもが別クラスだし、AはあくまでIインターフェースを使用しているのだから、その実態がBでもCでも関係が無い。

どういう形でも実現が可能だが、この実装をした場合に、影響範囲を最低限に抑える事が出来る。
例えD機能、E機能と拡張機能が追加される事があっても、D/Eそれぞれについて、クラスとIインターフェース作成時の分岐を追加するだけで、実装を終える事が出来る。
**とても拡張が簡単になる。**



機能拡張に必要な作業は、以下の2つとして纏められる

* 新機能クラスを追加
* インターフェース作成時に、新機能クラス用の分岐を追加
  * インターフェースの作成場所は、それ専用のクラスとする


### まとめ

* **「拡張の可能性が高いコードをインターフェースの向こう側に置いておく」事で、拡張が容易になる**
  * **これを実現するためには、クラスをインターフェース経由で呼び出すように実装しておく**


※注
（インターフェースではなく、facadeパターンを使う手もあった。この構成であればそれだけで良いのかも）
（インターフェースは引数ではなく、factoryクラスで実態クラスを分岐させる方が綺麗な気もする）


## インターフェースまとめ

インターフェースの目的は以下の2つ

* **「変更の可能性が高いコードをインターフェースの向こう側に閉じ込めてしまう」事で、保守を容易にする。**
* **「拡張の可能性が高いコードをインターフェースの向こう側に置いておく」事で、拡張を容易にする。**


そのために必要な実装方針は、以下の2つ

* **変更の可能性が高いコードは、直接使用するのではなくインターフェース経由で呼び出す。**
* **拡張の可能性が高いコードは、直接使用するのではなくインターフェース経由で呼び出す。**