Day 45 のゴールと全体像
Day 45 のテーマは 多態性(ポリモーフィズム)を「実践する」ことです。
キーワードはとてもシンプルです。
同じメソッド ↓ 異なる動作
これを、実際のコードで体験していきます。
ここまでで、
- 継承
virtual/override- 抽象クラス
- インターフェース
を学んできましたが、 それらが「多態性」というひとつのゴールに向かっていた、 という流れを感じてもらえると嬉しいです。
多態性とは何かをもう一度整理する
一言でいうと「同じ呼び方で、違う動き」
多態性(ポリモーフィズム)は、
「同じメソッド名で呼んでいるのに、 実際のオブジェクトの種類によって動きが変わること」
です。
たとえば、
animal.Speak()と呼ぶと、- 犬なら「ワン!」
- 猫なら「ニャー!」
- 鳥なら「チュンチュン」
というふうに、 同じ Speak() なのに、動きが違う。
これが多態性です。
ステップ1:基底クラス+virtual で「共通の窓口」を作る
動物クラス Animal を用意する
まずは、共通の「窓口」となる基底クラスを作ります。
using System;
// 基底クラス:動物
class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
// 仮想メソッド:鳴く
// 「動物なら必ず鳴く」という共通の窓口
public virtual void Speak()
{
Console.WriteLine($"{Name} は何か鳴きます。");
}
}
C#ここでのポイントは、
public virtual void Speak()→ 「動物は鳴く」という 共通のメソッド名 を用意している- 中身は「とりあえずのデフォルト実装」
というところです。
ステップ2:派生クラス+override で「動きを変える」
犬クラス Dog
// 犬クラス:Animal を継承
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
// Speak を犬用にオーバーライド
public override void Speak()
{
Console.WriteLine($"{Name} は『ワン!』と鳴きます。");
}
}
C#猫クラス Cat
// 猫クラス:Animal を継承
class Cat : Animal
{
public Cat(string name) : base(name)
{
}
// Speak を猫用にオーバーライド
public override void Speak()
{
Console.WriteLine($"{Name} は『ニャー!』と鳴きます。");
}
}
C#鳥クラス Bird
// 鳥クラス:Animal を継承
class Bird : Animal
{
public Bird(string name) : base(name)
{
}
// Speak を鳥用にオーバーライド
public override void Speak()
{
Console.WriteLine($"{Name} は『チュンチュン』と鳴きます。");
}
}
C#ここでの重要ポイントは、
- どのクラスもメソッド名は
Speakのまま - 中身だけがクラスごとに違う
というところです。
ステップ3:同じメソッド呼び出しで異なる動作を体験する
Animal 型でまとめて扱う
多態性を一番実感できるのは、 「同じ型として扱っているのに、動きが違う」瞬間です。
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
// Animal 型のリストに、Dog / Cat / Bird をまとめて入れる
List<Animal> animals = new List<Animal>
{
new Dog("ポチ"),
new Cat("タマ"),
new Bird("ピーちゃん")
};
Console.WriteLine("=== 動物たちが一斉に鳴きます ===");
foreach (var animal in animals)
{
// 型は Animal、呼び出すメソッドは常に Speak
// でも、実際の動きはクラスごとに違う
animal.Speak();
}
Console.ReadLine();
}
}
C#実行すると、
- ポチ は『ワン!』
- タマ は『ニャー!』
- ピーちゃん は『チュンチュン』
というふうに、 同じ animal.Speak() なのに、動きが変わります。
これが、Day 45 のテーマである
同じメソッド ↓ 異なる動作
の、まさに実例です。
ステップ4:抽象クラス+抽象メソッドで「仕様を強制する」
抽象クラス版 Animal
もう少し設計寄りにすると、 「動物なら必ず Speak() を持っていてほしい」という仕様を 抽象クラスで表現できます。
// 抽象クラス:動物の共通仕様
abstract class AbstractAnimal
{
public string Name { get; set; }
public AbstractAnimal(string name)
{
Name = name;
}
// 抽象メソッド:必ず実装しなければならない
public abstract void Speak();
// 共通処理
public void Introduce()
{
Console.WriteLine($"私は {Name} です。");
}
}
C#具体クラス Dog / Cat / Bird
class AbstractDog : AbstractAnimal
{
public AbstractDog(string name) : base(name) { }
public override void Speak()
{
Console.WriteLine($"{Name} は『ワン!』と鳴きます。");
}
}
class AbstractCat : AbstractAnimal
{
public AbstractCat(string name) : base(name) { }
public override void Speak()
{
Console.WriteLine($"{Name} は『ニャー!』と鳴きます。");
}
}
class AbstractBird : AbstractAnimal
{
public AbstractBird(string name) : base(name) { }
public override void Speak()
{
Console.WriteLine($"{Name} は『チュンチュン』と鳴きます。");
}
}
C#使う側はこうです。
class Program
{
static void Main(string[] args)
{
List<AbstractAnimal> animals = new List<AbstractAnimal>
{
new AbstractDog("ポチ"),
new AbstractCat("タマ"),
new AbstractBird("ピーちゃん")
};
foreach (var animal in animals)
{
animal.Introduce(); // 共通処理
animal.Speak(); // クラスごとに違う動き
}
Console.ReadLine();
}
}
C#ここでも、
Introduceは共通処理Speakは「同じメソッド名で異なる動作」
という構造になっています。
ステップ5:インターフェースで「役割ごとの多態性」を作る
IShape インターフェース
今度は「図形」を例にして、 インターフェースで多態性を実践してみます。
// 図形のインターフェース:面積を計算できるという契約
interface IShape
{
double GetArea();
}
C#円と四角形の実装
using System;
// 円
class Circle : IShape
{
public double Radius { get; }
public Circle(double radius)
{
Radius = radius;
}
public double GetArea()
{
// 円の面積 = πr^2
return Math.PI * Radius * Radius;
}
}
// 四角形
class Rectangle : IShape
{
public double Width { get; }
public double Height { get; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public double GetArea()
{
// 四角形の面積 = 幅 × 高さ
return Width * Height;
}
}
C#同じメソッドで異なる動作を実践する
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
List<IShape> shapes = new List<IShape>
{
new Circle(5.0),
new Rectangle(4.0, 3.0),
new Circle(2.5)
};
Console.WriteLine("=== 図形の面積を計算します ===");
foreach (var shape in shapes)
{
// 型は IShape、呼び出すメソッドは常に GetArea
// でも、実際の計算式はクラスごとに違う
double area = shape.GetArea();
Console.WriteLine($"面積: {area:F2}");
}
Console.ReadLine();
}
}
C#ここでも、
- 呼び出しているメソッドは常に
GetArea() - 実際の計算式は、円と四角形で違う
という「同じメソッド → 異なる動作」が実現されています。
多態性がもたらすメリットを整理する
1. 呼び出し側のコードがシンプルになる
animal.Speak()shape.GetArea()task.Execute()
など、 「何をしてほしいか」だけを書けばよくなります。
実際にどう動くかは、 クラスごとの実装に任せられます。
2. 新しい種類を追加しても、既存コードをほとんど変えなくてよい
- 新しい動物
Cowを追加しても、List<Animal>やforeach (animal.Speak())はそのまま使えます。 - 新しい図形
Triangleを追加しても、List<IShape>やshape.GetArea()はそのままです。
これは、アプリが大きくなったときに、 保守性・拡張性を劇的に高めてくれる考え方です。
3. 「役割」でコードを考えやすくなる
- 「鳴くことができるもの」
- 「面積を計算できるもの」
- 「実行できるタスク」
といった 「役割」 を、 抽象クラスやインターフェースで表現し、 多態性でまとめて扱うことができます。
多態性の基本テンプレート
最後に、 「同じメソッド → 異なる動作」を実現するための 基本テンプレートをまとめておきます。
抽象クラス版
// 抽象クラス:共通仕様
abstract class BaseAction
{
public string Name { get; }
protected BaseAction(string name)
{
Name = name;
}
// 抽象メソッド:必ず実装する
public abstract void Execute();
}
C#// 具体クラスA
class EmailAction : BaseAction
{
public EmailAction() : base("メール送信") { }
public override void Execute()
{
Console.WriteLine("メールを送信しています...");
}
}
// 具体クラスB
class ReportAction : BaseAction
{
public ReportAction() : base("レポート生成") { }
public override void Execute()
{
Console.WriteLine("レポートを生成しています...");
}
}
C#// 使う側
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
List<BaseAction> actions = new List<BaseAction>
{
new EmailAction(),
new ReportAction()
};
foreach (var action in actions)
{
Console.WriteLine($"アクション: {action.Name}");
action.Execute(); // 同じメソッド名で異なる動作
}
Console.ReadLine();
}
}
C#Day 45 のまとめ
Day 45 では、
- 同じメソッド名で呼んでいるのに、クラスごとに違う動きをする という「多態性」を、 動物・図形・タスクの例で実践しました。
キーワードはシンプルです。
同じメソッド ↓ 異なる動作
その裏側には、
- 継承
virtual/override- 抽象クラス
- インターフェース
といった仕組みが、 きれいに組み合わさっています。
ぜひ、
- 自分で「共通のメソッド名」を決めて
- それを持つクラスを複数作り
- リストや配列にまとめて、同じメソッドを呼んでみる
という小さな練習を通して、 多態性を 「教科書の言葉」ではなく、 自分のコードの中で生きている感覚として、 少しずつ育てていってください。
