はじめに
オブジェクト指向の重要な概念として、ポリモーフィズム (多態性) というものがあります。
これまでみてきた
- abstract
- virtual
- interface
はすべて ポリモーフィズムを実現するための仕組み になります。
今回は、ポリモーフィズムについて 説明していきます。
はじめに こうちゃ こんにちは、こうちゃです。 これまで、abstract・virtual・interface について説明してきました。 しかし、クラス設計をしていると、これら3つの違いがわからなくなってくるこ[…]
- ポリモーフィズムとはなにか
- abstract・virtual・interface とポリモーフィズムの関係
- クラスを作成したい方
- オブジェクト指向に興味のある方
ポリモーフィズムとは
例
Enemy enemy1 = new Boss();
Enemy enemy2 = new Mob();
enemy1.Attack();
enemy2.Attack();
この場合、
- 型:Enemy
- 実体:Boss、Mob
です。
enemy1、enemy2 インスタンスは、外見的には「Enemy」クラスになります。
ですが内面はそれぞれ「Boss」「Mob」クラスという状態です。
結果的に、
- 見た目は「Enemy クラスの Attack メソッドが呼び出された」
- 実際の動作は「Boss クラス、および Mob クラスの Attack メソッドが呼び出された」
という状態です。
このように、見た目は同じだが、実際の挙動は異なる ものがポリモーフィズムです。
ポリモーフィズムの重要性
冒頭で、ポリモーフィズムはオブジェクト指向において重要な概念だという話をしました。
では、どのように重要なのでしょうか。
重要性①:コードを共通化できる
1つ目は、コードが共通化できる点です。
List<Enemy> enemies = new List<Enemy>();
上記の「enemies」には、「Enemy」クラスを継承しているインスタンスはすべて追加可能です。
例えば「Mob」「Boss」「Dragon」クラスなどのインスタンスが追加できます。
foreach (var enemy in enemies)
{
enemy.Attack();
}
上記のように、「enemies」をループして、各要素に対して「Attack」メソッドを呼ぶことが可能になります。
先ほども言ったように、「enemies」には「Enemy」クラスを継承しているインスタンスはすべて追加できます。
そのため、「enemies」をループするだけで、実体を気にせず「Attack」メソッドを呼ぶことができます。
重要性②:条件分岐が不要になる
重要性①が可能になることにより、条件分岐が不要になり、コードがわかりやすくなります。
仮にポリモーフィズムを使わない場合、以下のような条件分岐が必要になります。
if (enemy is Mob) ...
if (enemy is Boss) ...
「Attack」メソッドのみならこれでも良いかもしれません。
しかし「敵キャラ」に共通する機能が増えるほど、この書き方はしんどくなっていきます。
重要性③:拡張に強い
「Enemy」クラスを継承した新たなクラスが作成された場合でも、以下の1行を追加するだけで良くなります。
enemies.Add(new NewEnemy());
重要性①のように、「enemies」をループさせてメソッドを呼んでいるため、新しいクラスのインスタンスを「enemies」に追加するだけで期待する動作をしてくれます。
abstract・virtual・interface とポリモーフィズム
abstract・virtual・interface とポリモーフィズムの関係についてみていきましょう。
abstract とポリモーフィズム
役割
必ず動作が異なる ことを保証する
例
abstract class Enemy
{
public abstract void Attack();
}
class Boss : Enemy
{
public override void Attack()
{
Console.WriteLine("強力な攻撃!");
}
}
class Mob : Enemy
{
public override void Attack()
{
Console.WriteLine("通常攻撃");
}
}
ポイント
- すべてのクラスで必ず「Attack」メソッドを実装
- 必ず動作が異なる
→ポリモーフィズムを強制する
virtual とポリモーフィズム
役割
必要に応じて異なる動作 にできる
例
class Enemy
{
public virtual void Attack()
{
Console.WriteLine("通常攻撃");
}
}
class Boss : Enemy
{
public override void Attack()
{
Console.WriteLine("強力な攻撃!");
}
}
ポイント
- override しなければ親クラスと同じ動作
- override すれば異なる動作
→柔軟なポリモーフィズム
interface とポリモーフィズム
役割
共通の呼び出し方法 を定義できる
例
interface IAttack
{
void Attack();
}
class Enemy : IAttack
{
public void Attack()
{
Console.WriteLine("攻撃");
}
}
class Player : IAttack
{
public void Attack()
{
Console.WriteLine("プレイヤー攻撃");
}
}
List<IAttack> attackers = new List<IAttack>
{
new Enemy(),
new Player()
};
foreach (var attacker in attackers)
{
attacker.Attack();
}
ポイント
- クラスが異なっていても同じように呼び出すことができる
→型を超えたポリモーフィズム
各概念とポリモーフィズムの関係まとめ
| 概念 | ポリモーフィズムとの関係 |
| abstract | 動作の違いを強制する |
| virtual | 動作の違いを任意にする |
| interface | 呼び出し方法を統一する |
まとめ
ポリモーフィズムは 同じ呼び出し方で異なる動作をする仕組み になります。
そして、
- abstract → 強制的に異なる動作を作る
- virtual → 柔軟に異なる動作を作る
- interface → 共通の呼び出し方を作る
という違いがそれぞれありました。
重要なのは、abstract・virtual・interface はどれもポリモーフィズムを実現する方法 ということです。
さいごに
今回はポリモーフィズムについて説明しました。
「ポリモーフィズム」という用語が出てきましたが、内容としてはこれまでの記事で記載してきたもののまとめだったと思います。
abstract・virtual・interface をそれぞれ説明した時点で、実際にはポリモーフィズムを使っていたということを理解していただけたでしょうか。
新しい用語が出ると身構えてしまいますが、もうすでに知っている内容だと安心しますよね。
ここまでくると、クラス (オブジェクト指向) の基本的な部分は触れたことになります。
もちろんもっと奥は深いですが、これまでの内容から発展していくに過ぎません。
どんどんマニアックになっていきますね。(笑)
以降のクラスに関わる記事は、個人的に有用だった内容を取り扱っていきます!
今回はここまで♪
楽しいプログラミングライフを!