技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

javaでのTemplate Methodパターン

Template Methodパターンは、処理(メソッド)の中で共通している箇所を抽象クラスとして抜き出すことで、重複した記述を排除し、更に処理の流れも規定する手法のことを指します。
手続き型言語でも共通する箇所を子メソッドとして抜き出すことをしますが、子メソッドとして抜き出してしまうとその子メソッドの使い方までは規定できず、子メソッドを誤った使い方をされてしまう可能性が出てきてしまいます。
 
サンプルコードとして、RPGのダメージ計算を模したプログラムを作成してみました。
「ベースのダメージ量に対してランダム化を行い最終的なダメージ量とする」という処理の流れを規定できていることがポイントです。
 
【サンプルコード】
・AbstractDamageCalc.java
import java.util.Random;

public abstract class AbstractDamageCalc {

    public abstract int getDamageBase();

    public final int damageCalc() {
        // ベースのダメージ量に7/8、8/8、9/8の何れかの値をかけてランダム化する
        // 単に共通処理をまとめるだけでなく、共通処理の使い方も指定できる
        // (ランダム化忘れ、ランダム化を2重でする、等を未然に防げる)
        return getDamageBase() * (new Random().nextInt(3) + 7) / 8;
    }
}
 
・PhysicsDamageCalc.java
public final class PhysicsDamageCalc extends AbstractDamageCalc {

    private int baseDamage = 0;

    public PhysicsDamageCalc(int physicsPower) {
        baseDamage = physicsPower / 2; //攻撃力の半分がダメージ
    }

    public int getDamageBase() {
        return baseDamage;
    }

}
 
・MagicDamageCalc.java
public final class MagicDamageCalc extends AbstractDamageCalc {

    private int baseDamage = 0;

    public MagicDamageCalc(int magicID) {
        if (magicID == 0) { //魔法「FireL1」
            baseDamage = 15;
        } else if (magicID == 1) { //魔法「FireL2」
            baseDamage = 60;
        } else if (magicID == 2) { //魔法「FireL3」
            baseDamage = 120;
        } else { //例外処理
            System.out.println("指定された魔法IDは未実装です");
            System.exit(1);
        }
    }

    public int getDamageBase() {
        return baseDamage;
    }

}
 
・DamageCalcMain.java
public class DamageCalcMain {
    public static void main(String[] args){

        int physicsPower = -1;
        int magicID = -1;
        int damage = 0;

        // 引数から物理攻撃力、魔法IDを取得
        if (args.length == 2) {
            physicsPower = Integer.parseInt(args[0]);
            magicID = Integer.parseInt(args[1]);
        }

        // ダメージ計算
        if (physicsPower >= 0 || magicID == -1) { //物理攻撃
            damage = new PhysicsDamageCalc(physicsPower)
                              .damageCalc();
        } else if (physicsPower == -1 || magicID >= 0) { //魔法攻撃
            damage = new MagicDamageCalc(magicID)
                              .damageCalc();
        } else { //例外処理
            System.out.println
                ("第1引数:物理攻撃力、魔法攻撃の場合は-1にしてください");
            System.out.println
                ("第2引数:魔法ID、物理攻撃の場合は-1にしてください");
            System.exit(1);
        }

        System.out.println("ダメージは"+damage);
        System.exit(0);

    }
}
 
【実行結果】 ※第1引数に-1、第2引数に1を指定した場合
ダメージは52