3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ポケモンで学ぶオブジェクト指向〜基礎編〜

Last updated at Posted at 2026-01-15

ピカチュウをコードで表現してみよう

1. はじめに:なぜポケモンで学ぶのか?

オブジェクト指向(OO:Object Oriented)は「現実世界をコードで表現する」考え方です。
そしてポケモンの世界は実はOOの概念だらけです。

  • ポケモン図鑑→クラス(設計図)
  • 実際のポケモン→インスタンス(実体)
  • 個体値→カプセル化(隠蔽されたデータ)
  • どのポケモンも「鳴く」→ポリモーフィズム

今回は「ピカチュウを1匹作る」ところから始めて、OOの基礎を体験していきましょう。

2. クラスとインスタンス

図鑑=クラス
個体=インスタンス

ポケモン図鑑には「ピカチュウとはこういうポケモンである」という定義が書いてあります。
これがクラスです。
そして、サトシが実際に連れているピカチュウは、その定義から生まれた「個体」です。

// ポケモン図鑑No.25の「設計図」
class Pikachu {
  name: string;
  level: number;
  hp: number;

  constructor(level: number) {
    this.name = "ピカチュウ";
    this.level = level;
    this.hp = 35 + level; // レベルに応じたHP
  }
}

// サトシのピカチュウ(個体)
const satoshiPikachu = new Pikachu(50);
console.log(satoshiPikachu.level); // 50

// 野生のピカチュウ(別個体)
const wildPikachu = new Pikachu(5);
console.log(wildPikachu.level); // 5

同じ「ピカチュウ」でも、サトシのピカチュウとトキワの森の野生のピカチュウは別物です。
設計図(クラス)は同じだけど、個体(インスタンス)ごとにレベルや状態が違います。

3. カプセル化:個体値は誰にも見せない

ポケモンには「個体値」という隠しステータスがあります。
ゲーム内では直接見ることができず、ジャッジ機能や外部ツールを使わないとわかりません。

また、HPも勝手に書き換えられたら困りますよね。ダメージを受けたり回復したり、決まった手順を踏んで変化するべきです。

このように「外から直接触らせたくないデータを隠し、決まった方法でだけ操作させる」のがカプセル化です。

class Pokemon {
  public name: string;
  private individualValue: number; // 個体値(外部からアクセス不可)
  private currentHp: number;
  private maxHp: number;

  constructor(name: string, maxHp: number) {
    this.name = name;
    this.maxHp = maxHp;
    this.currentHp = maxHp;
    this.individualValue = Math.floor(Math.random() * 32); // 0〜31でランダム
  }

  // HPを見るのはOK
  get hp(): number {
    return this.currentHp;
  }

  // ダメージを受ける(HPを減らす唯一の方法)
  receiveDamage(damage: number): void {
    this.currentHp = Math.max(0, this.currentHp - damage);
    
    if (this.currentHp === 0) {
      console.log(`${this.name}は倒れた...`);
    }
  }

  // 回復する
  heal(amount: number): void {
    this.currentHp = Math.min(this.maxHp, this.currentHp + amount);
    console.log(`${this.name}のHPが回復した!`);
  }

  // ジャッジ機能(個体値の判定だけ公開)
  judge(): string {
    if (this.individualValue === 31) return "さいこう";
    if (this.individualValue >= 26) return "すばらしい";
    if (this.individualValue >= 16) return "かなりいい";
    return "まあまあ";
  }
}
const pikachu = new Pokemon("ピカチュウ", 100);

// ✅ 許可された操作
pikachu.receiveDamage(30);
console.log(pikachu.hp);        // 70
console.log(pikachu.judge());   // "かなりいい" など

// ❌ これはできない(コンパイルエラー)
// pikachu.currentHp = 9999;     // チート不可!
// pikachu.individualValue = 31; // 個体値改ざん不可!

なぜカプセル化が必要でしょうか?

  • currentHp = -100 のような不正な値を防げる
  • HP変更時に「瀕死判定」などのロジックを必ず通せる
  • 内部実装を変えても、外部のコードに影響しない

4. 継承:ポケモンの共通点をまとめる

ピカチュウもイーブイも「ポケモン」です。
共通する性質は親クラスにまとめましょう。

abstract class Pokemon {
  constructor(
    public name: string,
    protected level: number,
    protected hp: number
  ) {}

  // 全ポケモン共通の処理
  showStatus(): void {
    console.log(`${this.name} Lv.${this.level} HP:${this.hp}`);
  }

  // 鳴き声は各ポケモンで違う → 抽象メソッド
  abstract cry(): void;
}

class Pikachu extends Pokemon {
  constructor(level: number) {
    super("ピカチュウ", level, 35 + level);
  }

  cry(): void {
    console.log("ピカチュウ!");
  }

  // ピカチュウ固有の技
  thunderbolt(): void {
    console.log("10まんボルト!");
  }
}

class Eevee extends Pokemon {
  constructor(level: number) {
    super("イーブイ", level, 55 + level);
  }

  cry(): void {
    console.log("ブイ!");
  }
}
const pikachu = new Pikachu(25);
const eevee = new Eevee(10);

pikachu.showStatus(); // ピカチュウ Lv.25 HP:60
pikachu.cry();        // ピカチュウ!

eevee.showStatus();   // イーブイ Lv.10 HP:65
eevee.cry();          // ブイ!

ポイント

  • abstract class は直接インスタンス化できない(new Pokemon() は不可)
  • abstract cry() は「鳴けることは決まっているが、鳴き方は各自で決めて」という宣言
  • 共通処理(showStatus)は親が書いておけば、子は書かなくて良い

5. ポリモーフィズム:ポケモンならみんな鳴ける

ポケモンセンターでは、どんなポケモンが来ても対応できます。
ピカチュウ専用の対応、イーブイ専用の対応と個別に用意する必要はありません。

function pokemonCenterGreeting(pokemon: Pokemon): void {
  console.log("ようこそ!ポケモンセンターへ");
  pokemon.cry();  // どのポケモンでもOK
  pokemon.showStatus();
}

// どのポケモンを渡しても動く
pokemonCenterGreeting(new Pikachu(25));
// ようこそ!ポケモンセンターへ
// ピカチュウ!
// ピカチュウ Lv.25 HP:60

pokemonCenterGreeting(new Eevee(10));
// ようこそ!ポケモンセンターへ
// ブイ!
// イーブイ Lv.10 HP:65

これが ポリモーフィズム(多態性) です。
pokemonCenterGreetingが親クラスであるPokemonを受け取っているところがポイントです。

// ポケモンの配列も作れる
const party: Pokemon[] = [
  new Pikachu(50),
  new Eevee(45),
  new Pikachu(30),  // ピカチュウ2匹目もOK
];

// 手持ち全員の鳴き声
party.forEach(pokemon => pokemon.cry());
// ピカチュウ!
// ブイ!
// ピカチュウ!

ポイント

  • 呼び出す側は「ポケモンは鳴ける」という共通ルールだけ知っていればOK
  • 中身がピカチュウでもイーブイでも気にしない

6. まとめ

概念 ポケモンでの例 TypeScriptでの表現
クラス ポケモン図鑑の定義 class Pokemon {}
インスタンス 実際のポケモン個体 new Pikachu(25)
カプセル化 個体値の隠蔽 private, getter/setter
継承 全ポケモン共通の性質 extends, abstract
ポリモーフィズム 「ポケモン」として統一的に扱う 親クラスの型で受け取る

次回予告

基礎はバッチリですね!
でも、今回のコードをじっと見てみるといくつか気になる点が出てきませんか?

■タイプってどう表現する?

  • ピカチュウはでんきタイプ
  • ギャラドスはみず・ひこうタイプ
  • リザードンはほのお・ひこうタイプ

継承で表現すると、ElectricPokemonWaterFlyingPokemon
タイプの組み合わせは100通り以上、全部クラスを作る?

■技の覚え方、これでいいの?

class Pikachu extends Pokemon {
  thunderbolt(): void {
    console.log("10まんボルト!");
  }
}

今の設計だと、ピカチュウに「アイアンテール」を覚えさせたくなったら、Pikachuクラスを修正することになります。
でも実際のゲームでは、同じピカチュウでも個体によって覚えている技は違いますよね。
サトシのピカチュウと、あなたのピカチュウは、きっと違う技構成のはず。

これらの「違和感」の正体は、先人たちが発見したOOの原則にあります。

次回「OO原則編」では、タイプと技の設計を通じて、より柔軟な設計手法を学んでいきます。

次回学ぶこと

  • 変化する部分をカプセル化する
  • 継承よりコンポジションを好む
  • インターフェースに対してプログラミングする

お楽しみに!

シリーズ目次

本記事は「ポケモンで学ぶオブジェクト指向」シリーズの一部です。

  1. 基礎編:ピカチュウをコードで表現してみよう
  2. OO原則編:継承だけでは表現できない世界
  3. SOLID原則編:5つの原則で設計を磨く
  4. デザインパターン編:名前を知れば会話ができる
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?