【SOLID原則】オープンクローズドの原則とは

## はじめに
社内で設計の勉強会を行った際に、前提であるSOLID原則
特にオープンクローズドの原則について理解が必要だと再確認したので、今回記事にまとめます。

分かりやすいように具体例とコードを交えて説明します。

## オープンクローズの原則とは
SOLID原則の1つの`Open Closed Principle`のことである。
ソフトウェアの構成要素は**拡張に対して開かれて**いて、**修正に対して閉じて**いなければならないという原則です。

- 拡張に対して開かれている
    - 機能の拡張
    - 新たなコードを追加することで機能を拡張することができる
- 修正に対して閉じている
    - 修正は機能を拡張するための既存コードの修正
    - 拡張によって既存コードが修正されない

つまり...ソフトウェアのふるまいは**既存の成果物を変更せず**に、
拡張できるようにするべきということです。

ただ、これだけでは具体的なイメージが湧きにくいので、
具体例を挙げます。

### オープン・クローズの原則を適用すべきケース

- 種類によって振る舞いの変更が必要な場合
  - ゲームのランク
    - ブロンズ
    - シルバー
    - ゴールド

さて、コードの方も書いていきます。

### 実際のコード

```mermaid
classDiagram
    direction BT
    class Prize {
        +getPrize(user: User)
    }

    class User {
        -name:string
        +rank: "bronze" | "silver" | "gold"
    }

    User <-- Prize
```

コード A

```ts
getPrize(user: User): string {
    switch(user.rank) {
        case "bronze":
            return "ブロンズ賞";
        case "silver":
            return "シルバー賞";
        case "gold":
            return "ゴールド賞";
    }
}
```

```mermaid
classDiagram
    direction BT
    class Prize {
        +getPrize(user: User)
    }

    class User {
        -name:string
        +rank: "bronze" | "silver" | "gold" | "platinum"
    }

    User <-- Prize
```

コード B

```ts
getPrize(user: User): string {
    switch(user.rank) {
        case "bronze":
            return "ブロンズ賞";
        case "silver":
            return "シルバー賞";
        case "gold":
            return "ゴールド賞";
        case "platinum":
            return "プラチナ賞";
    }
}
```

コード A は安定して動いていた際に、コード B で新たにプラチナ賞を追加した場合、
条件分岐を間違えるとバグが生むが可能性があります

オープンクローズドの原則を解決するなら、拡張の可能性のあるものを抽象化し、
具体の種別は抽象を実装する形にすると良いです。

```mermaid
classDiagram
    direction BT
    class User {
        <<interface>>
        -name:string
        +getPrize()
    }

    class BronzeUser {
        -name:string
        +getPrize()
    }

    class SilverUser {
        -name:string
        +getPrize()
    }

    class GoldUser {
        -name:string
        +getPrize()
    }


    User
    BronzeUser --|> User
    SilverUser --|> User
    GoldUser --|> User
```

実際に増やしていくと、Bronze,Silver,Gold のコードには変更が必要ありません

```mermaid
classDiagram
    direction BT
    class User {
        <<interface>>
        -name:string
        +getPrize()
    }

    class BronzeUser {
        -name:string
        +getPrize()
    }

    class SilverUser {
        -name:string
        +getPrize()
    }

    class GoldUser {
        -name:string
        +getPrize()
    }

    class PlatinumUser {
        -name:string
        +getPrize()
    }


    User
    BronzeUser --|> User
    SilverUser --|> User
    GoldUser --|> User
    PlatinumUser --|> User
```

```ts
interface User {
    name: string;
    getPrize(): string;
}

class BronzeUser implements User {
    constructor(public name: string) {}
    getPrize(): string {
        return "ブロンズ賞";
    }
}

class SilverUser implements User {
    constructor(public name: string) {}
    getPrize(): string {
        return "シルバー賞";
    }
}


class GoldUser implements User {
    constructor(public name: string) {}
    getPrize(): string {
        return "ゴールド賞";
    }
}

// クラスを追加するだけで、既存のコードには変更が必要ない
class PlatinumUser implements User {
    constructor(public name: string) {}
    getPrize(): string {
        return "プラチナ賞";
    }
}

const VALUE_CLASS_MAP = {
  bronze: BronzeUser,
  silver: SilverUser,
  gold: GoldUser,
  platinum: PlatinumUser
}

const getPrizeForUser = (rank:string, name:string) => {
    return new VALUE_CLASS_MAP[rank](name).getPrize()
}

console.log(getPrizeForUser("bronze", "takuya"))
console.log(getPrizeForUser("silver", "yuta"))
console.log(getPrizeForUser("gold", "taro"))
console.log(getPrizeForUser("platinum", "shota"))

```
以下が出力される

```
ブロンズ賞
シルバー賞
ゴールド賞
プラチナ賞
```

### 原則に違反すると?

- 既存のコードに修正を加えると、バグが生んでしまう可能性がある
  - 軽微な修正であってもケアレスミスをする可能性がある
- 既存のコードに対して再テストを行う工数が増える
  - 機能が増えてくるとテストの工数も増えていく

といったデメリットがあります。

## おわりに
オープンクローズドの原則は「新しい機能を追加した際に、既存のコードを変更しないように設計しよう」ということでした。

クラスをマッピングさせる、定数さえあればBronzeクラスを修正しても
他のクラスに影響がないのもオープンクローズド原則のメリットですね。

今回の記事が多くの方の参考になったら幸いです。

## 参考資料
https://qiita.com/getty104/items/41d4309dac1da41f14fc#%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF4-%E5%80%A4%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E5%B2%90%E3%81%AFhash%E3%81%A8strategy%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%82%92%E7%94%A8%E3%81%84%E3%82%8B


https://qiita.com/hankehly/items/1848f2fd8e09812d1aaf

https://ja.wikipedia.org/wiki/%E9%96%8B%E6%94%BE/%E9%96%89%E9%8E%96%E5%8E%9F%E5%89%87#:~:text=%E9%96%8B%E6%94%BE%2F%E9%96%89%E9%8E%96%E5%8E%9F%E5%89%87%EF%BC%88%E3%81%8B%E3%81%84%E3%81%BB%E3%81%86,%E3%81%A6%E3%81%84%E3%82%8B%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82

https://ja.wikipedia.org/wiki/Strategy_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

https://www.udemy.com/course/typescript-solid-design-patterns/