基礎から学ぶ継承とポリモーフィズム
C# で理解するオブジェクト指向の核心
目次
1. はじめに
オブジェクト指向(OOP)の 4 本柱のうち、継承 (Inheritance) と ポリモーフィズム (Polymorphism) は「共通化」と「拡張性」を支える最重要キーワードです。この記事では、まだ List<T> やラムダ式に触れていない初学者でもイメージしやすいように、動物クラスの例を通じてステップバイステップで解説します。
2. 継承 ―「親の特性を受け継ぐ」
2-1 継承のイメージ
- 比喩:「犬は動物の一種」という自然な関係をそのままコードに落とし込みます。
- 目的:
- コード再利用 – 共通コードは親クラスに一度だけ書く
- 階層化 – 大枠→詳細へ思考を整理できる
2-2 C# の基本構文
class Animal // 親(基底)クラス
{
public string Name { get; set; }
public void Eat() // 全動物共通の振る舞い
{
Console.WriteLine($"{Name} は食事をする");
}
}
class Dog : Animal // 子(派生)クラス
{
public void Bark()
{
Console.WriteLine($"{Name} はワン!と鳴く");
}
}
- Dog : Animal の : が 「Animal を継承」 を表します。
- Dog は Eat() を自動で持ち、独自メソッド Bark() を追加できます。
🛠 使い方(Console アプリ編)
- Visual Studio
- 「新しいプロジェクト」→ コンソール アプリ(.NET 8 以上推奨)を作成
- Animal.cs, Dog.cs, Program.cs の 3 ファイル構成にする
// Program.cs
using System;
class Program
{
static void Main()
{
var pochi = new Dog { Name = "ポチ" };
pochi.Eat(); // Animal のメソッド
pochi.Bark(); // Dog 独自メソッド
}
}
2-3 継承の注意点
落とし穴 | 具体例 | 回避策 |
---|---|---|
深すぎる階層 | Animal → Mammal → Canine → Dog → … と 6 段以上 | 3〜4 段で再設計を検討 |
is-a が成り立たない継承 | Bird : Airplane など | 共通化は インターフェースで代替 |
過剰な protected | 何でも派生先から触れる | private とプロパティで公開範囲を最小化 |
3. ポリモーフィズム ―「同じ呼び出しが多彩に振る舞う」
3-1 ポリモーフィズムとは
- ギリシャ語で 「多形」。
- 共通の型参照で実体に応じたメソッドが動く仕組み。
3-2 virtual / override による動的ポリモーフィズム
class Animal
{
public string Name { get; set; }
public virtual void Speak() // 上書き可
{
Console.WriteLine($"{Name} は何か声を出す");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine($"{Name} はワン!と鳴く");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine($"{Name} はニャーと鳴く");
}
}
- Speak() 呼び出し一つでDog と Cat が異なる鳴き声を出す。
- 拡張に強い:Bird を追加しても foreach 側は変更不要。
🛠 使い方(Console アプリ編)
- 先ほどと同様に Console プロジェクトを用意。
- Animal.cs, Dog.cs, Cat.cs, Program.cs を配置:
// Program.cs
using System;
class Program
{
static void Main()
{
Animal[] zoo =
{
new Dog { Name = "ポチ" },
new Cat { Name = "タマ" }
};
foreach (var a in zoo)
a.Speak(); // 実体に応じた出力が得られる
}
}
実行 →
ポチ はワン!と鳴く
タマ はニャーと鳴く
3-3 インターフェースによるポリモーフィズム
interface IFlyable
{
void Fly();
}
class Bird : Animal, IFlyable
{
public override void Speak() => Console.WriteLine($"{Name} はチュンと鳴く");
public void Fly() => Console.WriteLine($"{Name} が飛ぶ");
}
class Plane : IFlyable
{
public void Fly() => Console.WriteLine("飛行機が離陸する");
}
- 共通点は“飛べる”ことだけ。
- 2 つを IFlyable 配列に入れて Fly() を呼び出せば、実体ごとに適した挙動が発動。
🛠 使い方
using System;
class Program
{
static void Main()
{
IFlyable[] flyers =
{
new Bird { Name = "ピー" },
new Plane()
};
foreach (var f in flyers)
f.Fly();
}
}
4. 継承 × ポリモーフィズムを Unity で試す
シーン:AnimalController スクリプトを空の GameObject に付けて実行
4-1 スクリプト
// Animal.cs (Unity でも同じクラスを利用)
public class Animal
{
public string Name { get; set; }
public virtual void Speak() => Debug.Log($"{Name} は何か声を出す");
}
public class Dog : Animal
{
public override void Speak() => Debug.Log($"{Name} はワン!");
}
// Bird なども同様...
// AnimalController.cs
using UnityEngine;
public class AnimalController : MonoBehaviour
{
private void Start()
{
Animal[] animals =
{
new Dog { Name = "ポチ" },
new Cat { Name = "タマ" },
new Bird { Name = "ピー" }
};
foreach (var a in animals)
a.Speak();
}
}
🛠 使い方(Unity 6)
- Unity Hub で 3D Core テンプレートのプロジェクトを新規作成。
- Scripts フォルダーを作り、上記 .cs ファイルを保存。
- Hierarchy で Create → Empty。オブジェクト名は GameManager など。
- AnimalController.cs を GameManager にドラッグ&ドロップ。
- Play ボタン → Console に 3 行の鳴き声が表示されれば成功。
- コンソールに「ポチ はワン!」「タマ はニャー」「ピー はチュン」が順に表示。
- Unity のスクリプトでも同じ文法で動くことを確認できます。
5. 練習課題
- Fish クラスを Animal から継承し、Speak() を「はブクブク…」にする。
- IFlyable を実装しているクラスをシーン上でランダムに飛ばすミニゲームを作る。
- 「継承よりコンポジション(部品化)が適切」と思われるケースを 3 つ挙げる。
1. Fish クラスを実装し、Speak() を「〇〇はブクブク…」にする
class Fish : Animal
{
public override void Speak()
{
Console.WriteLine($"{Name} はブクブク…");
}
}
確認コード
var nemo = new Fish { Name = "ニモ" };
nemo.Speak(); // => ニモ はブクブク…
2. IFlyable を実装しているクラスをランダムに飛ばすミニゲーム(Unity)
- インターフェースと実装クラス
public interface IFlyable
{
void Fly(Vector3 direction, float speed);
}
public class Bird : Animal, IFlyable
{
public override void Speak() => Debug.Log($"{Name} はチュン");
public void Fly(Vector3 direction, float speed)
=> transform.Translate(direction.normalized * speed * Time.deltaTime);
}
public class Plane : MonoBehaviour, IFlyable
{
public void Fly(Vector3 direction, float speed)
=> transform.Translate(direction.normalized * speed * Time.deltaTime);
}
- コントローラースクリプト空の GameObject に追加してください。
using UnityEngine;
public class FlyManager : MonoBehaviour
{
[SerializeField] private MonoBehaviour[] flyables; // Bird, Plane など
private void Start()
{
InvokeRepeating(nameof(RandomFly), 1f, 2f); // 2 秒ごとに呼び出し
}
private void RandomFly()
{
if (flyables.Length == 0) return;
// ランダムに 1 体取得
var f = (IFlyable)flyables[Random.Range(0, flyables.Length)];
// ランダム方向と速度
Vector3 dir = Random.onUnitSphere; // 3D 空間なら
float speed = Random.Range(1f, 5f);
f.Fly(dir, speed);
}
}
ポイント
- 配列の型を MonoBehaviour にしているのは Unity エディタでドラッグ&ドロップ登録しやすくするため。
- 呼び出し側 (FlyManager) は IFlyable しか意識しておらず、Bird を追加してもコード変更不要です。
3. 継承よりコンポジションが適切なケース例
ケース | 継承では問題になる点 | コンポジションでの設計イメージ |
---|---|---|
① 武器を装備できるキャラクター | Swordman : Character, Archer : Characterなどを増やすとクラス爆発 | Character が Weapon を「持つ」。IWeapon インターフェースを介し、動的に交換可能 |
② UI のボタン装飾 | RedButton : Button, GreenButton : Button, … 色・大きさごとに派生が増える | Button に Style オブジェクトを注入し、色・枠線・サイズをパラメータ化 |
③ 複数の機能を組み合わせる家電 | SmartWasherDryerMicrowave : Applianceのような不自然な is-a 関係 | 小機能(洗う・乾かす・温める)を個別クラス化し、Appliance が必要な機能を「持つ」 |
判断の目安
- is-a(~は~である)があいまい / 無理やり
- 機能の組み合わせパターンが多い
- ランタイムに振る舞いを差し替えたい
その場合は コンポジション(部品化)+インターフェース を優先しましょう。
まとめ
サンプル回答を動かしながら次を試すと理解が深まります。
- Fish を Animal[] に混ぜて Speak() ポリモーフィズムを再確認
- IFlyable 実装クラスを増やし、FlyManager はノータッチで拡張できるか検証
- 自身のプロジェクトで「継承 vs コンポジション」を意識し、クラス設計を見直す
小さなサンプルでも “変更に強い” を体験することで、オブジェクト指向の本当のメリットが体に染み込みます。
6. まとめ
キーワード | 一言まとめ |
---|---|
継承 (Inheritance) | 「親の共通機能を子どもが受け継ぐ」 |
ポリモーフィズム (Polymorphism) | 「親型参照で子どもを自在に扱う」 |
- 継承は コード再利用と階層化、ポリモーフィズムは 拡張性と柔軟性 をもたらします。
- 理解のコツは、*「Dog は Animal である」*と *「Animal 配列の中身は何になるかわからない」*の 2 点を意識すること。
- まずはサンプルを コピペ→実行→改造 し、振る舞いが切り替わる瞬間を体験してみましょう。
次のステップ
- abstract クラスと sealed クラスの使いどころ
- インターフェース多重実装と DI (依存性注入) の基本
- 継承を避けたいケースの「コンポジション vs 継承」比較
OOP の旅はまだ始まったばかりですが、継承とポリモーフィズムを掴めれば、柔軟に拡張できる設計への第一歩を踏み出せます。ぜひ自分のプロジェクトで試してみてください。
訪問数 3 回, 今日の訪問数 1回
ディスカッション
コメント一覧
まだ、コメントがありません