ボタンひとつでわかるポリモーフィズム ― Enemy/Slime/Goblinで学ぶ
対象:最短でポリモーフィズムを体感したい初学者
ねらい:まずは「ボタンを押すだけ」の最小実装で、“同じ呼び出しでも実体により結果が変わる”を確認。その直後に実務寄りの改善版へ進みます。
ゴール
- 同じ型(Enemy)への同じ呼び出し(TakeDamage())でも、実体が Slime / Goblin で結果が変わることを体験する。
ソリューション名、プロジェクト名
PolySampleソリューション
PolySampleプロジェクト
で、作成し、GitHubDesktopで管理します
第1章:まずは基本アイデア(最小コード)
UI(WinForms)
- ボタン3つ:
- slime(テキスト:slime)…スライム選択
- goblin(テキスト:goblin)…ゴブリン選択
- button1(テキスト:こうげき)…攻撃実行 ※既定名のまま
コード(そのまま動く最小版)
using System;
using System.Windows.Forms;
namespace PolySample
{
public partial class Form1 : Form
{
Enemy enemy;
public Form1()
{
InitializeComponent();
}
private void slime_Click(object sender, EventArgs e)
{
enemy = new Slime();
}
private void goblin_Click(object sender, EventArgs e)
{
enemy = new Goblin();
}
private void button1_Click(object sender, EventArgs e) // 「こうげき」
{
enemy.TakeDamage();
}
}
class Enemy
{
public virtual void TakeDamage()
{
Console.WriteLine("ダメージを受けた(ダミー)");
}
}
class Slime : Enemy
{
public override void TakeDamage()
{
Console.WriteLine("スライムがダメージを受けた");
}
}
class Goblin : Enemy
{
public override void TakeDamage()
{
Console.WriteLine("ゴブリンがダメージを受けた");
}
}
}
表示先の注意:WinForms 実行中の Console.WriteLine は[出力] ウィンドウに出ます(実行→[表示]→[出力]/上部ドロップダウン「デバッグ」)。
あえて残している課題
- 敵未選択で攻撃すると NullReferenceException。
- 出力が画面上ではなく[出力]ウィンドウのみ。
第2章:学びやすさ&実務寄りに改善(命名を整える)
改善ポイント
- Null対策:敵未選択では攻撃不可(ボタン無効化&ガード)。
- UIでログ表示:ListBox にログを溜める。
- 抽象化の明確化:Enemy を abstract にして実装を子へ強制。
- 命名の整理:btnSlime, btnGoblin, btnAttack, lstLog, フィールドは _enemy。
追加UI
- ListBox:lstLog
- 起動時:btnAttack.Enabled = false
改善版コード(命名を統一)
using System;
using System.Windows.Forms;
namespace PolySample
{
public partial class Form1 : Form
{
private Enemy _enemy;
public Form1()
{
InitializeComponent();
btnAttack.Enabled = false; // 敵が決まるまで攻撃不可
}
private void btnSlime_Click(object sender, EventArgs e)
{
_enemy = new Slime();
Log("スライムがあらわれた!");
btnAttack.Enabled = true;
}
private void btnGoblin_Click(object sender, EventArgs e)
{
_enemy = new Goblin();
Log("ゴブリンがあらわれた!");
btnAttack.Enabled = true;
}
private void btnAttack_Click(object sender, EventArgs e)
{
if (_enemy == null)
{
Log("先に敵を選んでください。");
return;
}
string result = _enemy.TakeDamage(); // ポリモーフィズム
Log(result);
}
private void Log(string message) => lstLog.Items.Add(message);
}
// 子クラスに実装を強制して意図を明確化
public abstract class Enemy
{
public abstract string TakeDamage();
}
public class Slime : Enemy
{
public override string TakeDamage() => "スライムがダメージを受けた(ぷるぷる)";
}
public class Goblin : Enemy
{
public override string TakeDamage() => "ゴブリンがダメージを受けた(うめき声)";
}
}
改善版で伝えられること
- 安全性:未選択時の誤操作をUIで防ぐ/ガード節で二重に守る。
- 拡張容易性:Enemy の派生を増やしても、呼び出し側は基本変更不要。
- 責務分離:UIは「呼ぶだけ」、ふるまいはクラスが自分で持つ。
仕上げ:発展ミニ演習
- Dragon : Enemy を追加し、ボタン1つ追加だけで動くことを確認。
- Defend() を Enemy に追加し、各クラスで実装 → 画面に「ぼうぎょ」ボタンを追加。
了解です。前回の**改善版(命名を整えた完成形)**に合わせて、デザイナの配置図とモックのスクリーンショットを用意しました。最小版(button1の既定名のまま)についても配置図を併記します。
デザイナ配置図(最小版/button1は既定名)
コントロール | Name | Text | 初期状態 | 位置 (X,Y) | サイズ (W,H) | イベント |
---|---|---|---|---|---|---|
Button | slime | slime | Enabled: True | 30, 70 | 100, 32 | slime_Click |
Button | goblin | goblin | Enabled: True | 140, 70 | 100, 32 | goblin_Click |
Button | button1 | こうげき | Enabled: True | 250, 70 | 100, 32 | button1_Click |
Form | Form1 | PolySample | StartPosition: CenterScreen | ClientSize: 680×420 | — | — |
備考:この最小版はログ表示コントロールなし。Console.WriteLine の出力は Visual Studio の[出力]ウィンドウ(表示→出力/ドロップダウンで「デバッグ」)に出ます。
デザイナ配置図(改善版/完成フォーム)
コントロール | Name | Text | 初期状態 | 位置 (X,Y) | サイズ (W,H) | アンカー | イベント |
---|---|---|---|---|---|---|---|
Button | btnSlime | slime | Enabled: True | 30, 70 | 100, 32 | Top,Left | btnSlime_Click |
Button | btnGoblin | goblin | Enabled: True | 140, 70 | 110, 32 | Top,Left | btnGoblin_Click |
Button | btnAttack | こうげき | Enabled: False | 260, 70 | 120, 32 | Top,Left | btnAttack_Click |
ListBox | lstLog | (空) | — | 30, 120 | 620〜740, 260〜300 目安 | Top,Bottom,Left,Right | — |
Form | Form1 | PolySample | StartPosition: CenterScreen | ClientSize: 800×450 目安 | — | — |
配置のコツ
- btnAttack.Enabled = false を初期状態に。敵ボタンが押されたら true に切り替え。
- lstLog は四辺アンカーでフォーム拡大に追従。
- TabIndex は btnSlime:0 → btnGoblin:1 → btnAttack:2 → lstLog:3 を推奨。
完成フォーム「モック」スクリーンショット
- 最小版レイアウト(参考用・モック画像)PolySample_Minimal_Mock.png
- 改善版(完成形)レイアウト(初期状態/攻撃ボタン無効・モック画像)PolySample_Improved_Mock.png
注:環境に日本語フォントがない場合に備え、モック画像内のボタンラベルは ASCII で表記しています(例:btnAttack)。実際のフォームでは Text に日本語(こうげき など)を設定してください。
必要であれば、Form1.Designer.cs の InitializeComponent 完成版(座標・サイズ・アンカー・イベント配線まで含む)をそのまま貼り付けられる形でお渡しします。
ディスカッション
コメント一覧
まだ、コメントがありません