では「JavaScriptでのオブジェクト指向設計のベストプラクティス」として、継承よりコンポジションを優先する考え方を整理してみましょう。これは実務でもよく言われる「Composition over Inheritance(継承より合成を)」という原則です。
なぜ「継承よりコンポジション」なのか
- 継承の問題点
- 親子関係が強く結びつきすぎる(親を変えると子も壊れる)
- 階層が深くなると理解しづらい
- 「is-a」関係でないのに無理に継承すると不自然な設計になる
- コンポジションの利点
- 部品を組み合わせるだけなので柔軟
- 再利用性が高い(同じ部品をいろんなクラスに持たせられる)
- 依存関係が浅く、変更に強い
例題:ゲームキャラクターに「飛ぶ能力」を追加する
❌ 継承でやると…
class Character {
constructor(name){ this.name = name; }
}
class FlyingCharacter extends Character {
fly(){
console.log(`${this.name} が空を飛んだ!`);
}
}
let hero = new FlyingCharacter("勇者");
hero.fly(); // → 勇者 が空を飛んだ!
JavaScript👉 「飛べるキャラ」と「飛べないキャラ」を分けるためにクラスを増やすと、
「飛べる魔法使い」「飛べるモンスター」などが必要になり、クラス爆発が起きる。
✅ コンポジションでやると…
// 共通のキャラクター
class Character {
constructor(name){ this.name = name; }
}
// 「飛ぶ能力」を部品として定義
class FlyAbility {
fly(name){
console.log(`${name} が空を飛んだ!`);
}
}
// キャラクターに「飛ぶ能力」を持たせる
let hero = new Character("勇者");
hero.flyAbility = new FlyAbility();
hero.flyAbility.fly(hero.name);
// → 勇者 が空を飛んだ!
JavaScript👉 「飛ぶ能力」を部品として切り出すことで、
勇者でも魔法使いでもモンスターでも、必要なときだけ組み合わせられる。
✅ まとめ
- 継承は「is-a」関係(犬は動物の一種)で使う
- コンポジションは「has-a」や「can-do」関係(キャラは武器を持つ/飛ぶことができる)で使う
- 実務では「まずコンポジションを検討し、必要なときだけ継承」を選ぶのがベストプラクティス
💡 初心者へのアドバイス:
- 「これは本当にis-a関係か?」と考えてから継承を選ぶ
- 「能力・機能を追加したい」ならコンポジションで組み合わせる方が柔軟
