ポリモーフィズム

デザインパターンより、ポリモーフィズム
デザインパターンについてはそれについて書いてあるサイトやら本やらが山ほどあるのでそれを読めばいいと思う。簡単に言えば将棋やチェスでいう定石。

ポリモーフィズムとはプログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質を指す*1。なるほど、わからん。
ガンダムにたとえてみよう。ガンダムが分からなくても問題ない。
ガンダムは、武器を装備することができる。装備しているだけじゃダメなので、当然武器を使って攻撃する。ガンダムビームライフルを使って攻撃する、というのをC++で書くとこうなる。
(classが分からない人は、構造体に関数がくっついたものと思えばいい。構造体が分からない人はググれ)

class CGundam {
public:
  CBeamRifle MainWeapon;
  void Attack() {
    MainWeapon.Attack();
  }
};
CGundam Gundam;

分かるだろうか。アムロ君(ガンダムパイロット)がガンダムに攻撃しろ!(Gundam.Attack())と命令すると、自動的にメインウェポンであるビームライフルを使って攻撃してくれるのだ。
でも、ガンダムは何もビームライフルしか装備できないわけじゃない。ビームサーベルもハイパーバズーカもビームジャベリンもハイパーハンマーもあるのだ。それら全部を装備できるガンダムを作りたい、という欲張りな人のために、ポリモーフィズムというものはある。

/* 武器 */
class CWeapon {
public:
  int Ammo; // 残り弾数
  virtual void Attack();
};
// ビームライフル
class CBeamRifle : public CWeapon {
public:
  void Attack();
};
// ビームサーベル
class CBeamSaber : public CWeapon {
public:
  void Attack();
};
// ハイパーバズーカ
class CHyperBazooka : public CWeapon {
  int cartridge_left; // 残りカートリッジ
public:
  void Attack();
};

/* ガンダム */
class CGundam {
public:
  CWeapon *MainWeapon;
  void Equip(int id) {
    switch(id) {
      case 1: MainWeapon = new CBeamRifle(); break;
      case 2: MainWeapon = new CBeamSaber(); break;
      case 3: MainWeapon = new CHyperBazooka(); break;
    }
  }
  void Unattach() {
    delete MainWeapon;
  }
  void Attack() {
    MainWeapon->Attack();
  }
};
CGundam Gundam;

いきなり縦に伸びたが大したことではなし。まず、基底クラスと呼ばれるCWeaponを作る。武器だ。次に、このCWeaponクラスを継承した武器を沢山作る。継承というのは、CWeaponという属性を持ちつつ、詳細な情報や拡張機能を付け足すことができる便利な機能である。クラス名宣言の直後に「 : public CWeapon」と付け足すことにより、実現できる。
継承の何が嬉しいかというと、それはCGundamクラスにある。ガンダムのメインウェポンは、CWeapon型のポインタなのだ。CWeapon型のポインタには、CWeaponを継承しているもの(つまり武器)ならなんでも入れることができる。それをやっているのがEquip()で、指定したIDによって装備する武器の種類を変えている。ちなみに、継承前のCWeaponが持っていたint Ammo;という変数は、継承後のCBeamRifleなども使うことができる。
あとは、アムロGundam.Attack()と呼び出すことで、自動的に装備した武器を使って攻撃してくれる。終わり。

ワンポイントアドバイス
  • virtual void Attack();

virtualとは簡単に言えば、「これを継承したクラスによって関数が上書きされますよ」という宣言だ。これを使わないと、MainWeapon->Attack()を実行した時に呼び出される関数がCBeamRifleのものでなくCWeaponのものになってしまう。忘れずに指定するように。

  • CHyperBazookaのint cartridge_left;

ハイパーバズーカは実弾兵器なのでカートリッジの残り数が気になる所。しかし、その他の武器には要らないパラメータなので、CWeaponに書くのはちょっと……という感じだ。ここがポリモーフィズムの嬉しい点その2。武器によって勝手にパラメータを追加することができ、メモリや変数の数を節約することができる。勿論、関数も新しく作れる。

  • CGundamについて

とりあえずポリモーフィズムの説明のため最小構成だが、このソースコードはあまりにも危険である。アムロ君は武器を装備することを忘れてAttack()してしまうかもしれない。或いは武器を装備してるのに新しい武器を装備して、古い武器をなくしてしまうかもしれない。bool WeaponStateのような変数を作って管理してあげたり、スマートポインタなどを使ってあげる必要がある。