JavaでのTemplate Methodパターンの基本と実装方法

java

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);

    }
}

【実行結果】

ダメージは52

 ※第1引数に-1、第2引数に1を指定した場合


いかがでしたでしょうか。

手続き型言語に対するオブジェクト指向型言語の利点として、特定のルールに従ったコーディングを他の開発者にさせやすくなるというものがあり、バグの混入を未然に防ぐことができます。
共通処理の使い方を指定できるTemplate Methodパターンは、その利点が良く分かるパターンの一つだと思います。
抽象クラスの使い方として、このような使い方は覚えておいて損はないと思います。

では、また次回!

コメント

タイトルとURLをコピーしました