11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

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

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

オープンクローズの原則とは

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

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

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

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

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

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

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

実際のコード

コード A

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

コード B

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

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

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

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

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クラスを修正しても
他のクラスに影響がないのもオープンクローズド原則のメリットですね。

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

参考資料

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?