オブジェクト指向 ・ Chapter 12
継承より合成
記事 ・ 冒頭を無料公開中
この章でやること
これまでの仕上げです。ここまで継承を正しく使う方法を学んできましたが、この章では継承の限界を正面から見ます。そして、現代のエンジニアが使うこの原則を手に入れます。
継承より合成(Composition over Inheritance)。
継承が「〜である(is-a)」で作る設計だったのに対して、合成は「〜を持っている(has-a)」で作る設計です。結論から言うと、実務では合成が第一候補です。なぜそうなのかを、継承が壊れていく様子から体験します。
継承が壊れる瞬間: 組み合わせの爆発
ゲームのキャラクターで考えます。キャラクターには武器の種類があり、剣士と魔法使いがいます。継承で素直に作るとこうです。
class Character {
name: string;
constructor(name: string) {
this.name = name;
}
}
class SwordCharacter extends Character {
attack() {
console.log(this.name + "は剣で斬りつけた");
}
}
class MagicCharacter extends Character {
attack() {
console.log(this.name + "は魔法で攻撃した");
}
}
ここまでは平和です。では、仕様追加です。「剣も魔法も使える魔法剣士を追加したい」。
さっそく困ります。MagicSwordCharacter は SwordCharacter と MagicCharacter のどちらを継承すればいいのでしょう。両方は継承できません(親は1つだけ、でしたね)。仕方なく片方を継承してもう片方の実装をコピペしてみます。はい、継承が防ぐはずだったコピペの復活です。
さらに仕様追加が続きます。「移動手段として徒歩と騎乗を追加。どの武器とも組み合わせ可能」。継承ツリーで表現しようとすると:
SwordWalkCharacter(剣・徒歩)
SwordRideCharacter(剣・騎乗)
MagicWalkCharacter(魔法・徒歩)
MagicRideCharacter(魔法・騎乗)
MagicSwordWalkCharacter(魔法剣・徒歩)
MagicSwordRideCharacter(魔法剣・騎乗)
このチャプターの続きは受講生限定です
本文の残り、図解・実行できるコードブロック・演習はプラン加入後に読めます。