Bridgeパターンは、機能のクラス階層と実装(処理の中身)のクラス階層を橋渡しするパターンです。
機能のクラス階層と実装のクラス階層を分けることで、実装毎に機能を書かなくて良くなりますし、その逆に機能毎に実装を書かなくて良くなります。
結果として、重複した記述を減らすことができ、保守性を向上させることができます。
今回は、RPGのダメージ計算を模したサンプルコードを作成してみました。
機能のクラス階層ではダメージの与え方を定義し、実装のクラス階層では具体的なダメージ計算式を定義しています。
Bridgeパターンの適用により、ダメージの与え方毎にダメージ計算式を書かなくて良くなっている点、逆にダメージ計算式毎にダメージの与え方を書かなくて良くなっている点に注目です。
【サンプルコード】
・DamageCalc.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// ダメージ計算機能 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// ダメージ計算機能(連続攻撃に対応するための拡張) 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
1 2 3 4 5 6 |
// ダメージ計算の実装(抽象クラス) public abstract class DamageCalcImpl { public abstract int damageCalc(int offenceValue, int defenceValue); } |
・NormalDamageCalcImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// ダメージ計算の実装(通常のダメージ計算) 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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// ダメージ計算の実装(防御無視のダメージ計算) 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// ダメージ計算のメインクラス 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 2 3 4 5 6 |
■通常のダメージ計算(1回攻撃) ダメージ:75 ■防御無視のダメージ計算(1回攻撃) ダメージ:100 ■防御無視のダメージ計算(4回攻撃) ダメージ:400 |
いかがでしたでしょうか。
先日記事にさせていただいたBuilderパターンと似たような話ですが、こちらのパターンは、機能や処理の中身が少しずつ違う場合に冗長性を排除できるパターンです。
同じ記述をコピーすると保守性が低下しますので、同じ記述が続いてると思ったらこのパターンの適用を検討した方が良いと思います。
まだ紹介できていないデザインパターンがありますので、これからも記事にしていきたいと思います!
コメント