では「継承・コンポジション・Mixinの使い分け」という実務的な設計指針を整理してみましょう。これを押さえると、オブジェクト指向設計で迷いにくくなります。
3つのアプローチの特徴
| 手法 | 特徴 | 適した場面 | 注意点 |
|---|---|---|---|
| 継承 (extends) | 親クラスの性質をそのまま引き継ぐ | 「is-a」関係(犬は動物の一種) | 階層が深くなると複雑化、親の変更が子に波及 |
| コンポジション (has-a) | 部品を組み合わせて機能を作る | 「has-a」「can-do」関係(車はエンジンを持つ) | 設計が少し冗長になることもある |
| Mixin | 機能を後付けで追加できる | 複数のクラスに共通の能力を与えたいとき(飛ぶ・攻撃するなど) | 名前衝突や責務の分散に注意 |
実務でのベストプラクティス
- まずはコンポジションを検討する
- 柔軟で再利用性が高い
- 「部品を持つ」関係はほとんどコンポジションで表現できる
- is-a関係が明確なら継承を使う
- 例:
Dog extends Animal - ただし階層は浅く保つ(2〜3段階までが目安)
- 例:
- 共通の機能を横断的に付与したいならMixin
- 例:
FlyMixin,AttackMixinを複数のクラスに付与 - 「能力のパーツ」を貼り付けるイメージ
- 例:
例:ゲーム設計での使い分け
- 継承:
HeroやWizardはCharacterの一種 - コンポジション:
HeroはWeaponを持っている - Mixin:
HeroやMonsterに「飛ぶ能力」を後付け
class Character { constructor(name){ this.name = name; } }
class Hero extends Character {}
class Monster extends Character {}
class Weapon { constructor(name){ this.name = name; } }
let FlyMixin = {
fly(){ console.log(`${this.name} が空を飛んだ!`); }
};
Object.assign(Hero.prototype, FlyMixin);
Object.assign(Monster.prototype, FlyMixin);
let hero = new Hero("勇者");
hero.fly(); // → 勇者 が空を飛んだ!
JavaScript✅ まとめ
- 基本はコンポジション(柔軟で安全)
- 明確なis-a関係なら継承
- 横断的な機能はMixin
💡 初心者へのアドバイス:
「とりあえず継承で…」と考えるのではなく、まずはコンポジションで設計できないか?を考えるクセをつけると、後々の拡張や変更に強いコードになります。
