RPGでボスがプレイヤーのパーティに攻撃するようなイメージでインターフェースを実装

インターフェースの学習で考えられるコードは次のようになります
「Warrior」と「Wizard」という2つのキャラクターを含むリストがあり、「Boss」というキャラクターがそれらを攻撃するプログラムです。

List<IDamageable> characters = new List<IDamageable>()
        {
            new Warrior(),
            new Wizard()
        };

Boss boss = new Boss();

foreach (IDamageable character in characters)
{
    boss.Attack(character);
}


interface IDamageable
{
    void TakeDamage(int damage);
}

class Warrior : IDamageable
{
    public void TakeDamage(int damage)
    {
        Console.WriteLine($"戦士は{damage}のダメージを受けた");
    }
}

class Wizard : IDamageable
{
    public void TakeDamage(int damage)
    {
        Console.WriteLine($"魔導士は{damage}のダメージを受けた");
    }
}

class Boss
{
    public void Attack(IDamageable target)
    {
        target.TakeDamage(10);
    }
}

このC#コードは、「Warrior」と「Wizard」という2つのキャラクターを含むリストがあり、「Boss」というキャラクターがそれらを攻撃するプログラムです。

まず、IDamageableというインターフェイスが定義されています。このインターフェイスは、TakeDamageメソッドを定義しています。このメソッドは、キャラクターがダメージを受けたときに呼び出されます。

次に、WarriorWizardという2つのクラスが定義されています。この2つのクラスは、IDamageableインターフェイスを実装しています。このため、各クラス内でTakeDamageメソッドが実装されています。

次に、Bossクラスが定義されています。このクラスは、Attackメソッドを定義しています。このメソッドは、引数として渡されたIDamageableインターフェイスを実装したキャラクターに10のダメージを与えます。

最後に、Mainメソッドが定義されています。このメソッドでは、「Warrior」と「Wizard」という2つのキャラクターを含むリストが作成され、「Boss」がこのリスト内のキャラクターを1つずつ攻撃する処理が行われます。

インターフェースを使うのメリット

  1. 多様性: インターフェースを実装した複数のクラスが同じように扱われることができます。例えば、このコードではWarriorWizardは、IDamageableインターフェイスを実装しています。このため、同じように攻撃を受けることができます。
  2. 再利用性: インターフェースを実装したクラスは、他のクラスやメソッドから再利用することができます。例えば、このコードではBossクラスは、IDamageableインターフェースを実装したキャラクターを攻撃することができます。
  3. 抽象性: インターフェースは具体的な実装を持たないため、抽象的な概念を表現することができます。例えば、このコードではIDamageableインターフェイスは、「ダメージを受けることができるもの」という概念を表現しています。
  4. 型安全性: インターフェースを使うことで、型安全性を向上させることができます。例えば、このコードではAttackメソッドは、引数としてIDamageableインターフェイスを実装したものだけを受け付けます。このため、他のクラスや型を誤って渡すことができなくなります。

抽象クラスを定義して、Helthメンバーを追加するように変更

abstract class Character
{
    public int Health { get; set; }
}

class Warrior : Character, IDamageable
{
    public void TakeDamage(int damageAmount)
    {
        Health -= damageAmount;
    }
}

class Wizard : Character, IDamageable
{
    public void TakeDamage(int damageAmount)
    {
        Health -= damageAmount;
    }
}

class Boss
{
    public void Attack(IDamageable target)
    {
        target.TakeDamage(10);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var characters = new List<IDamageable>
        {
            new Warrior { Health = 100 },
            new Wizard { Health = 80 }
        };

        var boss = new Boss();
        foreach (var character in characters)
        {
            boss.Attack(character);
            Console.WriteLine($"{character.GetType().Name} Health: {character.Health}");
        }
    }
}

このコードは、「Warrior」と「Wizard」という2つのキャラクターを含むリストと、「Boss」というキャラクターがそれらを攻撃するプログラムです。

まず、Characterという抽象クラスがあります。このクラスには、キャラクターが持つHealthプロパティが定義されています。

次に、WarriorWizardクラスがあります。これらのクラスは、Characterクラスを継承しています。また、IDamageableというインターフェイスを実装しています。このインターフェイスは、キャラクターが攻撃を受けるときに使用するTakeDamageメソッドを定義しています。

次に、Bossクラスがあります。このクラスは、攻撃するキャラクターを引数として受け取り、そのキャラクターに対してTakeDamageメソッドを呼び出すことで攻撃を行います。

最後に、Programクラスがあります。このクラスは、Mainメソッドを持っており、アプリケーションのエントリポイントとなります。Mainメソッドでは、「Warrior」と「Wizard」という2つのキャラクターを含むリストを作成し、「Boss」によって攻撃を行います。攻撃後に、各キャラクターのHealthプロパティをコンソールに表示します。

抽象クラスの継承とインターフェースの実装のメリット

  1. インターフェースを使用することで、各キャラクターが共通の振る舞いを実装することができます。例えば、「Warrior」と「Wizard」が「Boss」の攻撃を受けることができるという振る舞いは、同じTakeDamageメソッドを使用することで実現されます。このように、各キャラクターの振る舞いをまとめることで、コードの保守性が向上します。
  2. 抽象クラスを使用することで、各キャラクターが共通のプロパティを持つことができます。例えば、「Warrior」と「Wizard」は共通のHealthプロパティを持つことができます。このように、各キャラクターが共通のプロパティを持つことで、コードの保守性が向上します。
  3. インターフェースと抽象クラスを使用することで、今後の要件変更にも柔軟に対応することができます。例えば、新しいキャラクターを追加する場合も、インターフェースを実装して振る舞いを定義することができます。また、新しいプロパティを追加する場合も、抽象クラスに追加することができます。このように、インターフェースと抽象クラスを使用することで、今後の要件変更にも柔軟に対応することができます。

抽象クラスの継承とインターフェースの実装のデメリット

このクラスのデメリットは以下のとおりです。

  1. 複雑性の増加: インターフェースと抽象クラスを使用することで、コードが複雑になる可能性があります。特に、初めてインターフェースや抽象クラスを使用する開発者にとっては理解するのが難しいかもしれません。
  2. パフォーマンス低下: インターフェースと抽象クラスを使用することで、パフォーマンスが低下する可能性があります。これは、インターフェースと抽象クラスを使用すると呼び出すメソッドが一層深くなり、呼び出すたびにオーバーヘッドが生じるからです。
  3. 制約の増加: インターフェースや抽象クラスを使用することで、開発者が実装する上での制約が増加する可能性があります。例えば、インターフェースを実装する際には必ず全てのメソッドを実装する必要があります。また、抽象クラスを継承する際には、継承元のクラスの振る舞いを適切に実装する必要があります。