プログラマー１年生がポリモーフィズムについて学んだのでＲＰＧで説明する。

※この記事は2018年01月15日に投稿された記事です。

---

オブジェクト指向プログラムのポリモーフィズムについて学んだのでまとめてみたいと思います。
以下を参考にしました。

- [基礎からしっかり学ぶC#の教科書C# 7対応](http://ec.nikkeibp.co.jp/item/books/P98940.html) - P.163（ポリモーフィズム～クラスを操作するには）
- [実戦で役立つ C#プログラミングのイディオム/定石&パターン](http://gihyo.jp/book/2017/978-4-7741-8758-7) - P.410（実践オブジェクト指向プログラミング）

##**ポリモーフィズムってなんだ？**

ポリモーフィズムとはオブジェクト指向プログラミングの概念の一つです。
日本語では多態性・多様性などと訳されます。
簡単に言うと同じ名前のメソッドを複数のクラスで使用できるようにし、そのメソッドを通して、暗黙的に複数のインスタンスの動作を切り替えることができるようにします。

例えば、JavaScriptでは以下のようなコードを書くことができます。

３つキャラクタークラスがあるとします。
キャラクター達はそれぞれ**`attack()`**メソッドを持っています。

```js:各キャラクタークラス
class Soldier {
    attack() {
        return '戦士は斬りかかった！';
    }
}

class Wizard {
    attack() {
        return '魔法使いは呪文を唱えた！';
    }
}

class Hunter {
    attack() {
        return '狩人は矢を放った！';
    }
}
```

上記のクラスのインスタンスを配列に格納し順に**`attack()`**メソッドを実行してみます。

```js:パーティーを作って攻撃させる
const party = [
    new Soldier(),
    new Wizard(),
    new Hunter(),
];
for (const character of party) {
    console.log(character.attack());
}
```

```console
戦士は斬りかかった！
魔法使いは呪文を唱えた！
狩人は矢を放った！
```

ここで大事なのは定数**`character`**の**`attack()`**メソッドが実行されていますが、実際に呼び出されているのは**`character`**に格納された**`Soldier`**、**`Wizard`**、**`Hunter`**の**`attack()`**メソッドであり、同じ**`attack()`**メソッドでも動作が切り替わっていることです。


これを静的型付け言語（C#）で書いてみるとどうでしょうか。

```csharp:jsのコードをそのままC\#にすると
using System;

public class Soldier {
    public string Attack() {
        return "戦士は斬りかかった！";
    }
}
public class Wizard {
    public string Attack() {
        return "魔法使いは呪文を唱えた！";
    }
}
public class Hunter {
    public string Attack() {
        return "狩人は矢を放った！";
    }
}

public class Program {
    static void Main() {
        var party = new Object[] {
            new Soldier(),
            new Wizard(),
            new Hunter(),
        };
        foreach (var character in party) {
            Console.WriteLine(character.Attack());
        }
    }
}
```

動きません。まず、コンパイルが通らない。
> CS1061: Type 'object' does not contain a definition for 'Attack' and no extension method 'Attack' of type 'object' could be found.

*CS1061*は、存在しないメソッドを呼び出そうとしたときや、存在しないクラス メンバーにアクセスしようとしたときに発生します。
配列にインスタンスを格納するところまでは良いのですが、「**`Object`**型には**`Attack()`**メソッドがないよ」と怒られているようです。

これを動くようにするには以下の２つ方法があります。

- **継承**を使う方法
- **インターフェース**を使う方法

順に見ていきたいと思います。

##**継承を使ったポリモーフィズム**

まず最初に、**`CharacterBase`**という基底クラスを定義します。
**abstract**が付加されたクラスは抽象クラスとなり**new**によるインスタンス化ができないクラスです。
抽象クラスは継承しインスタンス化できるクラスを定義することが前提になっています。

```csharp:CharacterBaseクラス
public abstract class CharacterBase {
    public virtual string Attack() {
        return "";
    }
}

// var character = new CharacterBase(); ←これは無理
```

続いて**CharacterBase**クラスを継承した各キャラクタークラスを定義します。

```csharp:CharacterBaseの具象クラス
public class Soldier : CharacterBase {
    public override string Attack() {
        return "戦士は斬りかかった！";
    }
}

public class Wizard : CharacterBase {
    public override string Attack() {
        return "魔法使いは呪文を唱えた！";
    }
}

public class Hunter : CharacterBase {
    public override string Attack() {
        return "狩人は矢を放った！";
    }
}
```

各キャラクタークラスでは、**override**キーワードを使い**`Attack()`**メソッドを再定義しています。
これで**`Attack()`**メソッドを持っているクラスを統一的に扱うことができるようになりました。
それでは、実際に上記のクラスを使ったコードは以下のようになります。

```csharp:ポリモーフィズムを使用したコード
using System;
using System.Collections.Generic;

public class Program {
    static void Main() {
        var party = new List<CharacterBase>() {
            new Soldier(),
            new Wizard(),
            new Hunter(),
        };
        foreach (CharacterBase character in party) {
            Console.WriteLine(character.Attack());
        }
    }
}
```

```console
戦士は斬りかかった！
魔法使いは呪文を唱えた！
狩人は矢を放った！
```

各キャラクタークラスのインスタンスを格納した**List\<T>**の要素の型がそれぞれの継承元である**`CharacterBase`**になっています。
**foreach**ブロックでは**`Soldier`**も**`Wizard`**も**`Hunter`**同じ**`CharacterBase`**クラスとみなされています。
しかし、呼び出される**`Attack()`**メソッドは**`CharacterBase`**の**`Attack()`**メソッドではなく、実際のインスタンスのものとなります。
つまり、実際の型が**`Soldier`**なら**`Soldier`**、**`Wizard`**なら**`Wizard`**、**`Hunter`**なら**`Hunter`**の**`Attack()`**メソッドが呼び出されています。

抽象クラスを使うと、異なる型のオブジェクトを同一視し、そのオブジェクトの型によって動作が切り替えることができるようになります。
このようにポリモーフィズムでは静的型つけ言語の厳密性を保ったまま、動的型つけ言語の柔軟性を得ることができます。

##**インターフェースを使ったポリモーフィズム**

インターフェースを使っても抽象クラスと同じことが可能です。
インターフェースは製品の規格のようなものでプロパティやメソッドの呼び出し方だけを定めたものです。


```csharp:ICharacterインターフェース
interface ICharacter {
    string Attack();
}
```

上記は**`ICharacter`**を実装したクラスには**string**を返す**`Attack()`**メソッドを定義しなければならないと定められていることを表します。
インターフェースには**public**と言ったアクセス修飾子は付けることはできません。

それでは次に、**`ICharacter`**クラスを実装した各キャラクタークラスを定義します。
インターフェースのメソッドやプロパティの具体的な動作はここに書くことになります。

```csharp:ICharacterインターフェースの実装
public class Soldier : ICharacter {
    public string Attack() {
        return "戦士は斬りかかった！";
    }
}

public class Wizard : ICharacter {
    public string Attack() {
        return "魔法使いは呪文を唱えた！";
    }
}

public class Hunter : ICharacter {
    public string Attack() {
        return "狩人は矢を放った！";
    }
}
```

これらのキャラクタークラスを利用するコードは以下のようになります。

```csharp
using System;
using System.Collections.Generic;

public class Program {
    static void Main() {
        var party = new List<ICharacter>() {
            new Soldier(),
            new Wizard(),
            new Hunter(),
        };
        foreach (ICharacter character in party) {
            Console.WriteLine(character.Attack());
        }
    }
}
```

利用する側のコードは**List\<T>**の要素の型が**`ICharacter`**になっただけで**`CharacterBase`**を継承した場合と同じです。


##**その他**

###**抽象メソッドの修飾子、`virtual`と`abstract`について**

抽象クラスの**`CharacterBase`**は以下のように書くことでもできます

```csharp:abstractを使った抽象メソッド
abstract class CharacterBase {
    public abstract string Attack();
}
```

抽象メソッドの修飾子が**abstract**の場合には実際の処理を記述しません。
メソッドの修飾子が**virtual**の場合にはオーバーライドしなくても構いませんが、**abstract**の場合には必ずオーバーライドし具体的な処理を定義する必要があります。


###**ポリモーフィズムは実際でどこでどう使うか**

ポリモーフィズムについて実際いつどこで役に立つのかと考えると、入力値のバリデーションなどに使えそうかなと思いました。

婚活サービスの登録フォームの検証で考えてみます。

ユーザーの入力値は以下のモデルにバインドされるとします。

```csharp:ユーザーの入力値がバインドされるモデル
public class Model {
    public string Name { get; set; }
    public DateTime Birth { get; set; }
    public bool Married { get; set; }
}
```

基底の検証クラスを定義します。

```csharp
public abstract class ValidatorBase {
    protected Model _model;
    
    public ValidatorBase(Model model) {
        this._model = model;
    }
    
    public abstract bool IsValid();
}
```

基底クラスを継承した各検証クラスを定義します。

```csharp
public class NameValidator : ValidatorBase {
    
    public NameValidator(Model model): base(model) { }
    
    // 入力されているかつ20文字以下なら可
    public override bool IsValid() {
        if (String.IsNullOrWhiteSpace(this._model.Name)) {
            return false;
        }
        if (this._model.Name.Length > 20) {
            return false;
        }
        return true;
    }
}

public class BirthValidator : ValidatorBase {
    
    public BirthValidator(Model model): base(model) { }
    
    // 20歳以上40歳以下なら可
    public override bool IsValid() {
        var age = DateTime.Now.Year - this._model.Birth.Year;
        if (DateTime.Now < this._model.Birth.AddYears(age)) {
            age--;
        }
        if (!(20 <= age && age <= 40)) {
            return false;
        }
        return true;
    }
}

public class MarriedValidator : ValidatorBase {
    
    public MarriedValidator(Model model): base(model) { }
    
    // 結婚していない人のみ可
    public override bool IsValid() {
        return !this._model.Married;
    }
}
```

上記クラスを利用したコードを書いてみます。

```csharp
using System;
using System.Collections.Generic;


public class Program {
    static void Main() {

        // ※実際にはユーザーの入力値がバインドされることを想定しています。
        var model = new Model() {
            Name = "名無しの権兵衛",
            Birth = new DateTime(1992, 7, 17),
            Married = false
        };

        var Validators = new List<ValidatorBase>() {
            new NameValidator(model),
            new BirthValidator(model),
            new MarriedValidator(model)
        };
        foreach (var validaotr in Validators) {
            // 順に検証して処理する
        }
        
    }
}
```

検証メソッドは**`Model`**に持った方が良いのかもしれません。
おそらく、あまり良い例ではないと思います。
アドバイスがありましたらよろしくお願いします。


###**抽象クラスとインターフェースの使い分け**

ポリモーフィズムは、抽象クラスとインターフェースを２つ方法で実現できました。
では、この２つはどのように使い分ければよいのでしょうか？

||抽象クラス|インターフェース|
|:---|:---|:---|
|具体的な実装|持つこともできる|持てない|
|多重継承|できない|できる|

調べると以下のようなことが書かれていました。

> 抽象クラスと派生クラスは「継承」の関係はIS A関係と呼ばれている対して、インターフェースと実装クラスの「実装」の関係はCAN DO関係と呼ばれているそうです。

> - 引用：[インタフェースと抽象クラスどっち使ったらいいんだ？](https://qiita.com/chkkchy/items/643eed88f59bd2b44d71)

> 鳥によっては鳴かない鳥、飛ばない鳥もいるでしょう。インターフェースが分かれているのと、インターフェースは多重継承できるという仕様上、継承するしない、複数継承といった使い分けができるのです。

> - 引用：[インターフェースと抽象クラスの使い分け、活用方法](https://qiita.com/IganinTea/items/e1d35db0a14a84bda452)


###**TypeScriptで書いてみる**

プライベートではJavaScriptよりTypeScriptを書く場面が多いのでTypeScriptで実装してみました。

```ts
abstract class CharacterBase {
    public attack(): string {
        return "";
    }
}

class Soldier extends CharacterBase {
    public attack(): string {
        return '戦士は斬りかかった！';
    }
}

class Wizard extends CharacterBase {
    public attack(): string {
        return '魔法使いは呪文を唱えた！';
    }
}

class Hunter extends CharacterBase {
    public attack(): string {
        return '狩人は矢を放った';
    }
}

const party: Array<CharacterBase> = [
    new Soldier(),
    new Wizard(),
    new Hunter(),
];

for (const c of party) {
    console.log(c.attack());
} 
```

※追記 2019/11/04

TypeScriptは構造的部分型（Structural Subtyping）ですので`CharacterBase`抽象クラスは必要ありません。