はじめに
社内で設計の勉強会を行った際に、前提である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クラスを修正しても
他のクラスに影響がないのもオープンクローズド原則のメリットですね。
今回の記事が多くの方の参考になったら幸いです。
参考資料