JavaでのBridgeパターンの実装方法と使用例

java

Bridgeパターンは、機能のクラス階層と実装(処理の中身)のクラス階層を橋渡しするパターンです。
機能のクラス階層と実装のクラス階層を分けることで、実装毎に機能を書かなくて良くなりますし、その逆に機能毎に実装を書かなくて良くなります。
結果として、重複した記述を減らすことができ、保守性を向上させることができます。

今回は、RPGのダメージ計算を模したサンプルコードを作成してみました。
機能のクラス階層ではダメージの与え方を定義し、実装のクラス階層では具体的なダメージ計算式を定義しています。
Bridgeパターンの適用により、ダメージの与え方毎にダメージ計算式を書かなくて良くなっている点、逆にダメージ計算式毎にダメージの与え方を書かなくて良くなっている点に注目です。

【サンプルコード】

・DamageCalc.java

// ダメージ計算機能
public class DamageCalc {

    protected DamageCalcImpl damageCalcImpl;
    protected int damage = 0;

    public DamageCalc (DamageCalcImpl damageCalcImpl) {
        this.damageCalcImpl = damageCalcImpl;
    }

    protected void clearDamage() {
        damage = 0;
    }

    public int getDamage() {
        return this.damage;
    }

    public void damageCalc(int offenceValue, int defenceValue) {
        clearDamage();
        damage = damageCalcImpl.damageCalc(offenceValue,defenceValue);
    }

}

・MultipleDamageCalc.java

// ダメージ計算機能(連続攻撃に対応するための拡張)
public class MultipleDamageCalc extends DamageCalc {

    public MultipleDamageCalc(DamageCalcImpl damageCalcImpl) {
        super(damageCalcImpl);
    }

    public void multipleDamageCalc
        (int offenceValue, int defenceValue, int damageTimes) {
        clearDamage();
        for(int i = 0;i < damageTimes;i++) {
            damage += damageCalcImpl.damageCalc(offenceValue,defenceValue);
        }
    }

}

・DamageCalcImpl.java

// ダメージ計算の実装(抽象クラス)
public abstract class DamageCalcImpl {

    public abstract int damageCalc(int offenceValue, int defenceValue);

}

・NormalDamageCalcImpl.java

// ダメージ計算の実装(通常のダメージ計算)
public class NormalDamageCalcImpl extends DamageCalcImpl {

    public int damageCalc(int offenceValue, int defenceValue) {
        int damage = 0;
        damage = offenceValue - (defenceValue/2);
        if (damage < 0) {
            damage = 0;
        }
        return damage;
    }

}

・IgnoreDefenceDamageCalcImpl.java

// ダメージ計算の実装(防御無視のダメージ計算)
public class IgnoreDefenceDamageCalcImpl extends DamageCalcImpl {

    public int damageCalc(int offenceValue, int defenceValue) {
        int damage = 0;
        damage = offenceValue;
        if (damage < 0) {
            damage = 0;
        }
        return damage;
    }

}

・DamageCalcMain.java

// ダメージ計算のメインクラス
public class DamageCalcMain {
    public static void main(String[] args){

        int offenceValue = 100;
        int defenceValue = 50;
        int multipleDamageTimes = 4; // 連続攻撃の場合のみ使用

        NormalDamageCalcImpl normalDamageCalcImpl =
            new NormalDamageCalcImpl();
        IgnoreDefenceDamageCalcImpl ignoreDefenceDamageCalcImpl =
            new IgnoreDefenceDamageCalcImpl();

        System.out.println("■通常のダメージ計算(1回攻撃)");
        DamageCalc damageCalc1 = new DamageCalc(normalDamageCalcImpl);
        damageCalc1.damageCalc(offenceValue, defenceValue);
        System.out.println(" ダメージ:" + damageCalc1.getDamage());

        // 実装を独立させることで、実装毎に機能を書かなくて良い
        // (通常の1回攻撃メソッドと
        //   防御無視の1回攻撃メソッドを用意しなくて良い)
        System.out.println("■防御無視のダメージ計算(1回攻撃)");
        DamageCalc damageCalc2 =
            new DamageCalc(ignoreDefenceDamageCalcImpl);
        damageCalc2.damageCalc(offenceValue, defenceValue);
        System.out.println(" ダメージ:" + damageCalc2.getDamage());

        // 機能を独立させることで、機能毎に実装を書かなくて良い
        // (1回攻撃の防御無視メソッドと
        //   複数攻撃の防御無視メソッドを用意しなくて良い)
        System.out.println("■防御無視のダメージ計算(4回攻撃)");
        MultipleDamageCalc multipleDamageCalc1 =
            new MultipleDamageCalc(ignoreDefenceDamageCalcImpl);
        multipleDamageCalc1.multipleDamageCalc
            (offenceValue, defenceValue, multipleDamageTimes);
        System.out.println(" ダメージ:" + multipleDamageCalc1.getDamage());

    }
}

【実行結果】

■通常のダメージ計算(1回攻撃)
 ダメージ:75
■防御無視のダメージ計算(1回攻撃)
 ダメージ:100
■防御無視のダメージ計算(4回攻撃)
 ダメージ:400

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

先日記事にさせていただいたBuilderパターンと似たような話ですが、こちらのパターンは、機能や処理の中身が少しずつ違う場合に冗長性を排除できるパターンです。
同じ記述をコピーすると保守性が低下しますので、同じ記述が続いてると思ったらこのパターンの適用を検討した方が良いと思います。

まだ紹介できていないデザインパターンがありますので、これからも記事にしていきたいと思います!

コメント

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