SOLID原則 - 依存性逆転の原則

## 概要
グループでSOLID原則とは何かを学んだので、その内容を記載します。
本記事では、SOLID原則の「依存性逆転の原則」について説明します。

### SOLID原則とは？
オブジェクト指向で用いられる以下5つの原則のことです。
* **S**ingle Responsibility Principle (単一責任の原則)
* **O**pen-Closed Principle (オープン・クローズドの原則)
* **L**iskov Substitution Principle (リスコフの置換原則)
* **I**nterface Segregation Principle (インターフェイス分離の原則)
* **D**ependency Inversion Principle (依存関係逆転の原則)　←本記事の内容

変更に強く、理解しやすいソフトウェアを作るための基本原則です。


## 依存性逆転の原則
実装の詳細を「抽象」に依存させることで、依存関係を逆転させること。
上位モジュールが下位モジュールに依存するのを減らすことを目的としており、
**疎結合なコード**に繋がります。

<details open><summary>疎結合なコードとは？</summary>

他のクラスやメソッドとの結びつきが弱い構造になっていること。
疎結合なコードは依存性が少なく、以下のメリットを持ちます。
1\.自身の機能を独立して維持できる
2\.再利用しやすい
3\.テストしやすい
4\.変更した際に、他に及ぼす影響が小さい
</details>

### 原則を守らないとどうなるか？
下位モジュールの修正が上位モジュール(依存先)に影響を及ぼす可能性があります。
依存性逆転の原則に則り、下位モジュールが上位モジュールに依存している場合は、下位モジュールのみ修正すれば影響が出ません◎

### 原則を守った実装方法
1\.下位モジュールの抽象化
2\.抽象化したものを上位モジュールに移動
3\.下位モジュールを上位モジュールが提供する抽象に依存させる

#### 具体的には...
`魔法使い(A)が炎攻撃(B)に依存(A→B)`

```plantuml
class 魔法使い{

}

class 炎攻撃{
  - 炎魔法で敵を攻撃
}

魔法使い ..> 炎攻撃 : 攻撃の依頼
@enduml
```
魔法使い(A)は炎攻撃(B)から情報を受け取り、それに基づいて攻撃を行っている状態。
つまり、魔法使い(A)が炎攻撃(B)に依存している状態になります。
でも炎攻撃だけでなく、氷攻撃を使って戦いたい時もある...。
　

**下位モジュールの抽象化**
そこで、まずは抽象化を行います。
`攻撃(B)のインターフェイスを作成(BI)`

```plantuml

interface 攻撃{
  - 〇〇魔法を使って敵を攻撃
}

@enduml
```
炎攻撃の抽象化、つまり攻撃のインターフェイスを作成します。
　

**抽象化したものを上位モジュールに移動**
そして、作成したインターフェイスを上位モジュールに移動させます。
`魔法使い(A)が攻撃(BI)のインターフェイスを提供(A→BI)`

```plantuml

package "A"{

    class 魔法使い{
    
    }
    
    interface 攻撃{
      - 〇〇魔法を使って敵を攻撃
    }
}

魔法使い ..> 攻撃 : 攻撃の依頼
@enduml
```
　
**下位モジュールを上位モジュールが提供する抽象に依存させる**
最後に、先ほど作成したインターフェイスに下位モジュールが依存するように実装します。
`魔法使い(A)が提供する攻撃インターフェイス(BI)に炎攻撃(B)を依存させる((A→BI)←B)`

```plantuml

package "A"{
    class 魔法使い{
    
    }
    
    interface 攻撃{
      - 〇〇魔法を使って敵を攻撃
    }
}

class 炎攻撃{
  - 炎魔法を使用
}

class 氷攻撃{  
  - 氷魔法を使用
}

魔法使い ..> 攻撃 : 攻撃の依頼
攻撃 <.. 炎攻撃 : 魔法の種類を指定
攻撃 <.. 氷攻撃

@enduml
```

炎攻撃(B)に依存している状態が解消され、氷攻撃も可能に！

実装の詳細(何の魔法を利用して攻撃するか)を「抽象」に依存させることにより「具象」への依存が解決され、疎結合なコードとなっています。
炎攻撃や氷攻撃だけでなくさまざまな種類の魔法に対応することが可能で、これは疎結合なコードのメリットである「再利用しやすい」に当てはまります。

依存の向きに注目すると、(A→B)から(A←B)になっているので、依存性逆転の原則と言います。

#### 抽象クラス、インターフェイスのどちらを用いる？
依存性逆転の原則は理解できたけど、実装時は抽象クラス/インターフェイスどちらを用いて抽象化すべきなの？という声がグループ内であがりました。
私たちのプロジェクトでは、より拡張性が高くなるためインターフェイスを用いる方が良さそうと結論が出ました。
とはいえ、抽象クラスとインターフェイスの元々の役割を考えると、インターフェイスへの依存が適切か疑問が残るため、まだまだ議論の余地がありそうです。

## 参考
* [Clean Code アジャイルソフトウェア達人の技](https://www.amazon.co.jp/Clean-Code-%E3%82%A2%E3%82%B8%E3%83%A3%E3%82%A4%E3%83%AB%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E9%81%94%E4%BA%BA%E3%81%AE%E6%8A%80-Robert-C-Martin/dp/4048930591/ref=pd_vtp_h_pd_vtp_h_d_sccl_4/358-3755681-0356521?pd_rd_w=lUUv2&content-id=amzn1.sym.a075abab-259f-40c3-bc0f-76d5f3149ef5&pf_rd_p=a075abab-259f-40c3-bc0f-76d5f3149ef5&pf_rd_r=QW6XVZC6VJG155QENZA8&pd_rd_wg=yP7Nx&pd_rd_r=1d275eed-97be-48f9-84a2-b9ece9468928&pd_rd_i=4048930591&psc=1)(書籍)
* [現実世界の事象から学ぶSOLID原則 by ほりしょー](https://fortee.jp/oocon-2024/proposal/e1eb34cf-78ef-43f6-8a03-bb26c996cb62)
* [マリオで学ぶSOLID原則](https://zenn.dev/keiichiro/articles/9397379ab638b1)
* [密結合と疎結合の理解を深める](https://qiita.com/Guz9N9KLASTt/items/e4ed966fc889d019c725)
* [依存性の逆転のいちばんわかりやすい説明](https://zenn.dev/naas/articles/c743a3d046fa78)
* [「依存性逆転の原則」と「依存性の注入」を完全に理解した](https://qiita.com/uhooi/items/03ec6b7f0adc68610426#%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87dip%E3%81%A8%E3%81%AF)