より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

基底クラスのループ処理の中で、特定のクラスでのメソッドを呼ぶ時の実装

リファクタリングの頭の体操のような話です。

基底クラスのループ処理の中で、特定のクラスでのメソッドを呼ぶ時の実装について、思ったのでメモ。

以下のコードは、

  • 抽象クラスのSpriteを用意
  • HumanとMagicianはSpriteクラスを継承して、実装
  • Magicianの時だけは魔法使いなので、Magicメソッドを実装
  • Human,Human,Magicianという配列をループさせて、その中で、Magicianの時にだけMagicメソッドを呼び出したい

といったコードです。

using System;
                    
public class Program
{
    public abstract class Sprite {
        public void Act() {
        }
    }
    
    public class Human:Sprite {
    }
    
    public class Magician:Sprite {
        public void Magic() {
            Console.WriteLine("Fire!");
        }
    }
    
    public static void Main()
    {
        var party = new Sprite[] {
            new Human(),
            new Human(),
            new Magician()
        };
        foreach (var sprite in party) {
                         sprite.Act();
            if (sprite is Magician magician) {
                magician.Magic();
            }
        }       
    }
}

ゲームだと、このようなコードを私を書くことが多かったです。

ただ、こういう時は

using System;
                    
public class Program
{
    public abstract class Sprite {
        public void Act() {
        }
    }
    
    public class Human:Sprite {
    }
    
    public class Magician:Sprite {
        // privateに出来る!!!
        private void Magic() {
            Console.WriteLine("Fire!");
        }
        public static void Magic(Sprite sprite) {
            if (sprite is Magician magician) {
                magician.Magic();
            }
        }
    }   
    
    public static void Main()
    {
        var party = new Sprite[] {
            new Human(),
            new Human(),
            new Magician()
        };
        foreach (var sprite in party) {
                         sprite.Act();
            Magician.Magic(sprite);
        }       
    }
}

と、Magicのクラスメソッドはprivateにして静的メソッドで呼べた方がいいですね。

もっと複雑になったら、基底クラスに新たなメソッドを作るとか、delegateを経由して呼び出されるようにするとか、考えることになるでしょう。d

どこまで頑張るかはケースバイケースですね。