徒然電脳

日々のプログラミング(とその他)忘備録 このサイトは独自研究のみに基づきます マサカリ歓迎しますというかよろしくお願いします

デザインパターン[TemplateMethod]

RPGゲーム

 RPGというと真・女神転生が好きだったりするが、今回はDQ7のような職業制のRPGについて考えてみる。
 戦闘シーンにおいて、職業が異なると攻撃方法が異なったりする。たとえば剣士なら剣で攻撃するし、弓使いは弓で攻撃する。
 このように、キャラクターに応じて攻撃方法などが異なるゲームを想定する。
 また、ここではオートモードについても考えてみる。
 オートモードのAIは簡単で、自身のHPが60以上なら攻撃を行なう。それより小さいと回復だ。これは全職業に共通するAIだとする。
 つまり、職業間で共通部分はオートモード機能。攻撃・回復は職業に依存している。
 こんかいは、そんな関係をTemplateMethodパターンっぽく書いてみようと思う。
 
 とりあえず、簡単なクラス図を。
 f:id:TempProg:20160220222428j:plain

Characterクラス

 全ての職業のキャラクターがCharacterクラスを継承する。
 

using System;

namespace TemplateMethod {
    abstract class Character {
        public int HP { get; set; }
        
        /// <summary>
        /// 何らかの方法で攻撃する。
        /// </summary>
        public abstract void Attack();

        /// <summary>
        /// 何らかの方法で回復する。
        /// </summary>
        public abstract void Heal();

        /// <summary>
        /// オートモードで戦闘する。
        /// </summary>
        public void DoAuto() {
            if (HP >= 60)
                Attack();
            else
                Heal();
        }

        public Character(int hp) {
            this.HP = hp;
        }
    }
}

 クラス図やコードを見て分かる通り、Characterクラスはabstractなクラスだ。
 AttackメソッドとHealメソッドがある。これらは職業によって異なる実装をしなければいけないので、子クラスに実装を任せている。
 そして、全職業に共通するオートモード機能はDoAutoメソッドが担っている。DoAutoメソッドでは抽象メソッドであるAttackメソッド、Healメソッドを使用している。これこそがTemplateMethodパターンの真髄だろう。DoAutoメソッドは枠組みのようなもので、明示的に実装されていながら、実際どのような動作になるかは子クラスに依存するのだ。

Swordsmanクラス

using System;

namespace TemplateMethod {
    class Swordsman : Character {
        public override void Attack() {
            Console.WriteLine("剣で攻撃!");
        }

        public override void Heal() {
            Console.WriteLine("薬草で回復!");
        }

        public Swordsman(int hp) : base(hp) { }
    }
}

 Swordsmanクラスは剣士を表すクラスだ。
 剣士は剣で攻撃するからAttackメソッドは「剣で攻撃する」という実装を行なう。
 また、回復スキルは持ち合わせていないから「薬草で回復」する。
 このように、攻撃・回復を実装するだけで、オートモード機能はCharacterクラスにあるのだから実装する必要が無い。再利用だ。
 と、このようにTemplateMethodパターンは継承をうまく使った例(悪く言えば当たり前な例)なのだろう。

Magicianクラス

 一応、魔法使いを表すクラスも記載しておく。ただし、やってることはSwordsmanクラスと同じだ。

using System;

namespace TemplateMethod {
    class Magician : Character {
        public override void Attack() {
            Console.WriteLine("黒魔法で攻撃!");
        }

        public override void Heal() {
            Console.WriteLine("白魔法で回復!");
        }

        public Magician(int hp) : base(hp) { }
    }
}

Programクラス

最後に、これらクラスの使用方法としてMainメソッドがあるProgramクラスの実装も記載する。

using System;

namespace TemplateMethod {
    class Program {
        static void Main(string[] args) {
            Character mga = new Magician(100);
            Character mgb = new Magician(40);
            Character swa = new Swordsman(100);
            Character swb = new Swordsman(40);

            mga.DoAuto();
            mgb.DoAuto();
            swa.DoAuto();
            swb.DoAuto();
        }
    }
}

このように、Character型の変数を使うのが良いと思われる。


今回、AttackメソッドもHealメソッドもpublicなメソッドとして定義した。しかし、設計によっては外部に公開したくない場合もある。そのような場合はprotectedなメソッドとして定義することで、子クラスでのオーバーライドも可能で、かつ外部からはアクセスできなくなる。